Skip to content

Commit 721f87a

Browse files
committed
+method: dcom vs-dte
1 parent a7cf802 commit 721f87a

2 files changed

Lines changed: 107 additions & 2 deletions

File tree

cmd/dcom.go

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ func dcomCmdInit() {
2424
dcomShellBrowserWindowCmdInit()
2525
dcomHtafileCmdInit()
2626
dcomExcelXlmCmdInit()
27+
dcomVsDteCmdInit()
2728

2829
dcomCmd.PersistentFlags().AddFlagSet(defaultAuthFlags.Flags)
2930
dcomCmd.PersistentFlags().AddFlagSet(defaultLogFlags.Flags)
@@ -34,6 +35,7 @@ func dcomCmdInit() {
3435
dcomShellBrowserWindowCmd,
3536
dcomHtafileCmd,
3637
dcomExcelXlmCmd,
38+
dcomVsDteCmd,
3739
)
3840
}
3941

@@ -134,12 +136,34 @@ func dcomExcelXlmCmdInit() {
134136
dcomExcelXlmCmd.MarkFlagsMutuallyExclusive("macro", "macro-file", "out")
135137
}
136138

139+
func dcomVsDteCmdInit() {
140+
dcomVsDteExecFlags := newFlagSet("Execution")
141+
dcomVsDteExecFlags.Flags.StringVar(&dcomVisualStudioDte.CommandName, "vs-command", "", "Visual Studio DTE command to execute")
142+
dcomVsDteExecFlags.Flags.StringVar(&dcomVisualStudioDte.CommandArgs, "vs-args", "", "Visual Studio DTE command arguments")
143+
registerExecutionFlags(dcomVsDteExecFlags.Flags)
144+
registerExecutionOutputFlags(dcomVsDteExecFlags.Flags)
145+
146+
cmdFlags[dcomVsDteCmd] = []*flagSet{
147+
dcomVsDteExecFlags,
148+
defaultAuthFlags,
149+
defaultLogFlags,
150+
defaultNetRpcFlags,
151+
}
152+
dcomVsDteCmd.Flags().AddFlagSet(dcomVsDteExecFlags.Flags)
153+
154+
// Constraints
155+
dcomVsDteCmd.MarkFlagsOneRequired("command", "exec", "vs-command")
156+
dcomVsDteCmd.MarkFlagsMutuallyExclusive("command", "exec", "vs-command")
157+
dcomVsDteCmd.MarkFlagsMutuallyExclusive("vs-command", "out")
158+
}
159+
137160
var (
138161
dcomMmc = dcomexec.DcomMmc{}
139162
dcomShellWindows = dcomexec.DcomShellWindows{}
140163
dcomShellBrowserWindow = dcomexec.DcomShellBrowserWindow{}
141164
dcomHtafile = dcomexec.DcomHtafile{}
142165
dcomExcelXlm = dcomexec.DcomExcelXlm{}
166+
dcomVisualStudioDte = dcomexec.DcomVisualStudioDte{}
143167

144168
dcomCmd = &cobra.Command{
145169
Use: "dcom",
@@ -243,7 +267,7 @@ var (
243267
Short: "Execute with the Excel.Application DCOM object using XLM macros",
244268
Long: `Description:
245269
The excel-xlm method uses the exposed Excel.Application DCOM object to call ExecuteExcel4Macro, thus executing
246-
XLM macros at will.`,
270+
XLM macros at will. This method requires that the remote host has Microsoft Excel installed.`,
247271
Args: args(argsRpcClient("host", ""), argsOutput("smb"),
248272
func(*cobra.Command, []string) error {
249273
if dcomExcelXlm.MacroFile != "" {
@@ -261,7 +285,7 @@ var (
261285
return nil
262286
},
263287
),
264-
Run: func(cmd *cobra.Command, args []string) {
288+
Run: func(*cobra.Command, []string) {
265289
dcomExcelXlm.Client = &rpcClient
266290
ctx := log.With().Str("module", dcomexec.ModuleName).Str("method", dcomexec.MethodExcelXlm).
267291
Logger().WithContext(gssapi.NewSecurityContext(context.Background()))
@@ -271,4 +295,22 @@ var (
271295
}
272296
},
273297
}
298+
299+
dcomVsDteCmd = &cobra.Command{
300+
Use: "vs-dte [target]",
301+
Short: "Execute with the VisualStudio.DTE object",
302+
Long: `Description:
303+
The vs-dte method uses the exposed VisualStudio.DTE object to spawn a process via the ExecuteCommand method.
304+
This method requires that the remote host has Microsoft Visual Studio installed.`,
305+
Args: args(argsRpcClient("host", ""), argsOutput("smb")),
306+
Run: func(*cobra.Command, []string) {
307+
dcomVisualStudioDte.Client = &rpcClient
308+
ctx := log.With().Str("module", dcomexec.ModuleName).Str("method", dcomexec.MethodVisualStudioDTE).
309+
Logger().WithContext(gssapi.NewSecurityContext(context.Background()))
310+
311+
if err := goexec.ExecuteCleanMethod(ctx, &dcomVisualStudioDte, &exec); err != nil {
312+
log.Fatal().Err(err).Msg("Operation failed")
313+
}
314+
},
315+
}
274316
)

pkg/goexec/dcom/visualstudio.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package dcomexec
2+
3+
import "github.com/rs/zerolog"
4+
5+
/*
6+
See https://learn.microsoft.com/en-us/dotnet/api/envdte._dte.executecommand
7+
*/
8+
9+
import (
10+
"context"
11+
12+
"github.com/FalconOpsLLC/goexec/pkg/goexec"
13+
"github.com/oiweiwei/go-msrpc/midl/uuid"
14+
15+
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/hresult"
16+
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus"
17+
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32"
18+
)
19+
20+
const (
21+
MethodVisualStudioDTE = "VisualStudio.DTE:ExecuteCommand"
22+
VisualStudioDteUuid = "33ABD590-0400-4FEF-AF98-5F5A8A99CFC3"
23+
)
24+
25+
type DcomVisualStudioDte struct {
26+
Dispatch
27+
// CommandName is the name of the DTE command to invoke
28+
CommandName string
29+
// CommandArgs are the arguments to pass to the command
30+
CommandArgs string
31+
}
32+
33+
func (m *DcomVisualStudioDte) Init(ctx context.Context) (err error) {
34+
if err = m.Dcom.Init(ctx); err == nil {
35+
return m.getDispatch(ctx, uuid.MustParse(VisualStudioDteUuid))
36+
}
37+
return
38+
}
39+
40+
func (m *DcomVisualStudioDte) Execute(ctx context.Context, execIO *goexec.ExecutionIO) (err error) {
41+
log := zerolog.Ctx(ctx)
42+
dteCmd := m.CommandName
43+
dteArgs := m.CommandArgs
44+
if dteCmd == "" {
45+
dteCmd = "tools.shell"
46+
dteArgs = execIO.String()
47+
}
48+
defer func() {
49+
// Terminate devenv.exe
50+
q, err := m.callComMethod(ctx, nil, "Quit")
51+
if err != nil {
52+
log.Warn().Err(err).Msg("Call to Quit() failed")
53+
err = nil
54+
}
55+
zerolog.Ctx(ctx).Info().Int32("return", q.Return).Msg("Quit called")
56+
}()
57+
log.Info().Str("command", dteCmd).Str("args", dteArgs).Msg("Executing DTE command")
58+
ir, err := m.callComMethod(ctx, nil, "ExecuteCommand", stringToVariant(dteArgs), stringToVariant(dteCmd))
59+
if err == nil {
60+
log.Info().Int32("return", ir.Return).Msg("ExecuteCommand called")
61+
}
62+
return
63+
}

0 commit comments

Comments
 (0)