both examples working

This commit is contained in:
Ronnie Flathers
2020-03-13 20:14:29 -05:00
parent 64b7790b2b
commit 78ec7ca314
19 changed files with 470 additions and 340 deletions
+6 -3
View File
@@ -1,9 +1,12 @@
package main
// +build windows
package clr
import (
"github.com/Microsoft/go-winio/pkg/guid"
"syscall"
"unsafe"
"github.com/Microsoft/go-winio/pkg/guid"
)
type AppDomain struct {
@@ -83,7 +86,7 @@ type AppDomainVtbl struct {
get_DynamicDirectory uintptr
}
func newAppDomain(ppv uintptr) *AppDomain {
func NewAppDomain(ppv uintptr) *AppDomain {
return (*AppDomain)(unsafe.Pointer(ppv))
}
+10 -3
View File
@@ -1,9 +1,12 @@
package main
// +build windows
package clr
import (
"github.com/Microsoft/go-winio/pkg/guid"
"syscall"
"unsafe"
"github.com/Microsoft/go-winio/pkg/guid"
)
// from mscorlib.tlh
@@ -16,6 +19,10 @@ type AssemblyVtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
GetTypeInfoCount uintptr
GetTypeInfo uintptr
GetIDsOfNames uintptr
Invoke uintptr
get_ToString uintptr
Equals uintptr
GetHashCode uintptr
@@ -62,7 +69,7 @@ type AssemblyVtbl struct {
get_GlobalAssemblyCache uintptr
}
func newAssembly(ppv uintptr) *Assembly {
func NewAssembly(ppv uintptr) *Assembly {
return (*Assembly)(unsafe.Pointer(ppv))
}
+73
View File
@@ -0,0 +1,73 @@
// +build windows
package main
import (
"fmt"
"log"
"syscall"
clr "github.com/ropnop/go-clr"
)
func must(err error) {
if err != nil {
log.Fatal(err)
}
}
func checkOK(hr uintptr, caller string) {
if hr != 0x0 {
log.Fatalf("%s returned 0x%08x", hr)
}
}
func main() {
metahost, err := clr.GetICLRMetaHost()
must(err)
fmt.Println("[+] Got metahost")
installedRuntimes, err := clr.GetInstalledRuntimes(metahost)
must(err)
fmt.Printf("[+] Found installed runtimes: %s\n", installedRuntimes)
versionString := "v4.0.30319"
pwzVersion, _ := syscall.UTF16PtrFromString(versionString)
var pRuntimeInfo uintptr
hr := metahost.GetRuntime(pwzVersion, &clr.IID_ICLRRuntimeInfo, &pRuntimeInfo)
checkOK(hr, "metaHost.GetRuntime")
runtimeInfo := clr.NewICLRRuntimeInfo(pRuntimeInfo)
fmt.Printf("[+] Using runtime: %s\n", versionString)
var isLoadable bool
hr = runtimeInfo.IsLoadable(&isLoadable)
checkOK(hr, "runtimeInfo.IsLoadable")
if !isLoadable {
log.Fatal("[!] IsLoadable returned false. Bailing...")
}
var pRuntimeHost uintptr
hr = runtimeInfo.GetInterface(&clr.CLSID_CLRRuntimeHost, &clr.IID_ICLRRuntimeHost, &pRuntimeHost)
checkOK(hr, "runtimeInfo.GetInterface")
runtimeHost := clr.NewICLRRuntimeHost(pRuntimeHost)
hr = runtimeHost.Start()
checkOK(hr, "runtimeHost.Start")
fmt.Println("[+] Loaded CLR into this process")
fmt.Println("[+] Executing assembly...")
pDLLPath, _ := syscall.UTF16PtrFromString("TestDLL.dll")
pTypeName, _ := syscall.UTF16PtrFromString("TestDLL.HelloWorld")
pMethodName, _ := syscall.UTF16PtrFromString("SayHello")
pArgument, _ := syscall.UTF16PtrFromString("foobar")
var pReturnVal *uint16
hr = runtimeHost.ExecuteInDefaultAppDomain(
pDLLPath,
pTypeName,
pMethodName,
pArgument,
pReturnVal)
checkOK(hr, "runtimeHost.ExecuteInDefaultAppDomain")
fmt.Printf("[+] Assembly returned: 0x%x\n", pReturnVal)
}
+109
View File
@@ -0,0 +1,109 @@
// +build windows
package main
import (
"fmt"
"io/ioutil"
"log"
"runtime"
"syscall"
"unsafe"
clr "github.com/ropnop/go-clr"
)
func must(err error) {
if err != nil {
log.Fatal(err)
}
}
func checkOK(hr uintptr, caller string) {
if hr != 0x0 {
log.Fatalf("%s returned 0x%08x", caller, hr)
}
}
func main() {
metaHost, err := clr.GetMetaHost()
must(err)
versionString := "v4.0.30319"
pwzVersion, _ := syscall.UTF16PtrFromString(versionString)
var pRuntimeInfo uintptr
hr := metaHost.GetRuntime(pwzVersion, &clr.IID_ICLRRuntimeInfo, &pRuntimeInfo)
checkOK(hr, "metahost.GetRuntime")
runtimeInfo := clr.NewICLRRuntimeInfo(pRuntimeInfo)
var isLoadable bool
hr = runtimeInfo.IsLoadable(&isLoadable)
checkOK(hr, "runtimeInfo.IsLoadable")
if !isLoadable {
log.Fatal("[!] IsLoadable returned false. Bailing...")
}
hr = runtimeInfo.BindAsLegacyV2Runtime()
checkOK(hr, "runtimeInfo.BindAsLegacyV2Runtime")
var pRuntimeHost uintptr
hr = runtimeInfo.GetInterface(&clr.CLSID_CorRuntimeHost, &clr.IID_ICorRuntimeHost, &pRuntimeHost)
runtimeHost := clr.NewICORRuntimeHost(pRuntimeHost)
hr = runtimeHost.Start()
checkOK(hr, "runtimeHost.Start")
fmt.Println("[+] Loaded CLR into this process")
var pAppDomain uintptr
var pIUnknown uintptr
hr = runtimeHost.GetDefaultDomain(&pIUnknown)
checkOK(hr, "runtimeHost.GetDefaultDomain")
iu := clr.NewIUnknown(pIUnknown)
hr = iu.QueryInterface(&clr.IID_AppDomain, &pAppDomain)
checkOK(hr, "iu.QueryInterface")
appDomain := clr.NewAppDomain(pAppDomain)
fmt.Println("[+] Got default AppDomain")
testEXEBytes, err := ioutil.ReadFile("./TestEXE.exe")
must(err)
runtime.KeepAlive(testEXEBytes)
fmt.Printf("[+] Loaded %d bytes into memory from TestExe.exe\n", len(testEXEBytes))
safeArray, err := clr.CreateSafeArray(testEXEBytes)
must(err)
runtime.KeepAlive(safeArray)
fmt.Println("[+] Crated SafeArray from byte array")
var pAssembly uintptr
hr = appDomain.Load_3(uintptr(unsafe.Pointer(&safeArray)), &pAssembly)
checkOK(hr, "appDomain.Load_3")
assembly := clr.NewAssembly(pAssembly)
fmt.Printf("[+] Executable loaded into memory at 0x%08x\n", pAssembly)
var pEntryPointInfo uintptr
hr = assembly.GetEntryPoint(&pEntryPointInfo)
checkOK(hr, "assembly.GetEntryPoint")
fmt.Printf("[+] Executable entrypoint found at 0x%08x. Calling...\n", pEntryPointInfo)
fmt.Println("-------")
methodInfo := clr.NewMethodInfo(pEntryPointInfo)
var pRetCode uintptr
nullVariant := clr.Variant{
VT: 1,
Val: uintptr(0),
}
hr = methodInfo.Invoke_3(
nullVariant,
uintptr(0),
&pRetCode)
fmt.Println("-------")
checkOK(hr, "methodInfo.Invoke_3")
fmt.Printf("[+] Executable returned code %d\n", pRetCode)
appDomain.Release()
runtimeHost.Release()
runtimeInfo.Release()
metaHost.Release()
}
+152
View File
@@ -0,0 +1,152 @@
// +build windows
package clr
import (
"fmt"
"strings"
"syscall"
)
var (
Metahost *ICLRMetaHost
RuntimeInfo *ICLRRuntimeInfo
IsLoadable bool
LegacyV2Runtime bool
CorRuntimeHost *ICORRuntimeHost
ClrRuntimeHost *ICLRRuntimeHost
Iu *IUnknown
LoadedAppDomain *AppDomain
LoadedAssembly *Assembly
EntryPoint *MethodInfo
)
func GetMetaHost() (*ICLRMetaHost, error) {
var pMetaHost uintptr
hr := CLRCreateInstance(&CLSID_CLRMetaHost, &IID_ICLRMetaHost, &pMetaHost)
err := checkOK(hr, "CLRCreateInstance")
if err != nil {
return nil, err
}
return NewICLRMetaHost(pMetaHost), nil
}
func GetInstalledRuntimes(metahost *ICLRMetaHost) ([]string, error) {
var runtimes []string
var pInstalledRuntimes uintptr
hr := metahost.EnumerateInstalledRuntimes(&pInstalledRuntimes)
err := checkOK(hr, "EnumerateInstalledRuntimes")
if err != nil {
return runtimes, err
}
installedRuntimes := NewIEnumUnknown(pInstalledRuntimes)
var pRuntimeInfo uintptr
var fetched = uint32(0)
var versionString string
versionStringBytes := make([]uint16, 20)
versionStringSize := uint32(len(versionStringBytes))
var runtimeInfo *ICLRRuntimeInfo
for {
hr = installedRuntimes.Next(1, &pRuntimeInfo, &fetched)
if hr != S_OK {
break
}
runtimeInfo = NewICLRRuntimeInfo(pRuntimeInfo)
if ret := runtimeInfo.GetVersionString(&versionStringBytes[0], &versionStringSize); ret != S_OK {
return runtimes, fmt.Errorf("GetVersionString returned 0x%08x", ret)
}
versionString = syscall.UTF16ToString(versionStringBytes)
runtimes = append(runtimes, versionString)
}
if len(runtimes) == 0 {
return runtimes, fmt.Errorf("Could not find any installed runtimes")
}
runtimeInfo.Release()
return runtimes, err
}
func GetRuntimeInfo(metahost *ICLRMetaHost, version string) (*ICLRRuntimeInfo, error) {
pwzVersion, err := syscall.UTF16PtrFromString(version)
if err != nil {
return nil, err
}
var pRuntimeInfo uintptr
hr := metahost.GetRuntime(pwzVersion, &IID_ICLRRuntimeInfo, &pRuntimeInfo)
err = checkOK(hr, "metahost.GetRuntime")
if err != nil {
return nil, err
}
return NewICLRRuntimeInfo(pRuntimeInfo), nil
}
func GetICLRRuntimeHost(runtimeInfo *ICLRRuntimeInfo) (*ICLRRuntimeHost, error) {
var pRuntimeHost uintptr
hr := runtimeInfo.GetInterface(&CLSID_CorRuntimeHost, &IID_ICLRRuntimeHost, &pRuntimeHost)
err := checkOK(hr, "runtimeInfo.GetInterface")
if err != nil {
return nil, err
}
return NewICLRRuntimeHost(pRuntimeHost), nil
}
// ExecuteDLL 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
func ExecuteDLL(dllpath, typeName, methodName, argument string) (retCode int, err error) {
retCode = -1
metahost, err := GetMetaHost()
if err != nil {
return
}
defer metahost.Release()
runtimes, err := GetInstalledRuntimes(metahost)
if err != nil {
return
}
var latestRuntime string
for _, r := range runtimes {
if strings.Contains(r, "v4") {
latestRuntime = r
break
} else {
latestRuntime = r
}
}
runtimeInfo, err := GetRuntimeInfo(metahost, latestRuntime)
if err != nil {
return
}
defer runtimeInfo.Release()
var isLoadable bool
hr := runtimeInfo.IsLoadable(&isLoadable)
err = checkOK(hr, "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
}
defer runtimeHost.Release()
hr = runtimeHost.Start()
err = checkOK(hr, "runtimeHost.Start")
if err != nil {
return
}
pDLLPath, _ := syscall.UTF16PtrFromString(dllpath)
pTypeName, _ := syscall.UTF16PtrFromString(typeName)
pMethodName, _ := syscall.UTF16PtrFromString(methodName)
pArgument, _ := syscall.UTF16PtrFromString(argument)
var pReturnVal *uint16
hr = runtimeHost.ExecuteInDefaultAppDomain(pDLLPath, pTypeName, pMethodName, pArgument, pReturnVal)
err = checkOK(hr, "runtimeHost.ExecuteInDefaultAppDomain")
if err != nil {
return int(*pReturnVal), err
}
return int(*pReturnVal), nil
}
+1 -3
View File
@@ -4,7 +4,5 @@ go 1.13
require (
github.com/Microsoft/go-winio v0.4.14
github.com/go-ole/go-ole v1.2.4
github.com/pkg/errors v0.9.1
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527
)
+6 -6
View File
@@ -1,17 +1,17 @@
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+18
View File
@@ -0,0 +1,18 @@
package clr
import (
"golang.org/x/sys/windows"
)
var (
CLSID_CLRMetaHost = windows.GUID{0x9280188d, 0xe8e, 0x4867, [8]byte{0xb3, 0xc, 0x7f, 0xa8, 0x38, 0x84, 0xe8, 0xde}}
IID_ICLRMetaHost = windows.GUID{0xD332DB9E, 0xB9B3, 0x4125, [8]byte{0x82, 0x07, 0xA1, 0x48, 0x84, 0xF5, 0x32, 0x16}}
IID_ICLRRuntimeInfo = windows.GUID{0xBD39D1D2, 0xBA2F, 0x486a, [8]byte{0x89, 0xB0, 0xB4, 0xB0, 0xCB, 0x46, 0x68, 0x91}}
CLSID_CLRRuntimeHost = windows.GUID{0x90F1A06E, 0x7712, 0x4762, [8]byte{0x86, 0xB5, 0x7A, 0x5E, 0xBA, 0x6B, 0xDB, 0x02}}
IID_ICLRRuntimeHost = windows.GUID{0x90F1A06C, 0x7712, 0x4762, [8]byte{0x86, 0xB5, 0x7A, 0x5E, 0xBA, 0x6B, 0xDB, 0x02}}
IID_ICorRuntimeHost = windows.GUID{0xcb2f6722, 0xab3a, 0x11d2, [8]byte{0x9c, 0x40, 0x00, 0xc0, 0x4f, 0xa3, 0x0a, 0x3e}}
CLSID_CorRuntimeHost = windows.GUID{0xcb2f6723, 0xab3a, 0x11d2, [8]byte{0x9c, 0x40, 0x00, 0xc0, 0x4f, 0xa3, 0x0a, 0x3e}}
IID_AppDomain = windows.GUID{0x5f696dc, 0x2b29, 0x3663, [8]uint8{0xad, 0x8b, 0xc4, 0x38, 0x9c, 0xf2, 0xa7, 0x13}}
)
+24 -21
View File
@@ -1,19 +1,20 @@
//+build windows
// +build windows
package main
package clr
import (
"github.com/Microsoft/go-winio/pkg/guid"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
var (
modMSCoree = syscall.MustLoadDLL("mscoree.dll")
procCLRCreateInstance = modMSCoree.MustFindProc("CLRCreateInstance")
modMSCoree = syscall.NewLazyDLL("mscoree.dll")
procCLRCreateInstance = modMSCoree.NewProc("CLRCreateInstance")
)
func CLRCreateInstance(clsid, riid *guid.GUID, ppInterface *uintptr) uintptr {
func CLRCreateInstance(clsid, riid *windows.GUID, ppInterface *uintptr) uintptr {
ret, _, _ := procCLRCreateInstance.Call(
uintptr(unsafe.Pointer(clsid)),
uintptr(unsafe.Pointer(riid)),
@@ -21,14 +22,16 @@ func CLRCreateInstance(clsid, riid *guid.GUID, ppInterface *uintptr) uintptr {
return ret
}
// GetICLRMetaHost
//func GetICLRMetaHost() (*ICLRMetaHost, error) {
// var ppInterface uintptr
// if hr := getICLRMetaHostPtr(&ppInterface); hr != S_OK {
// return nil, fmt.Errorf("Could not get pointer to ICLRMetaHost. HRESULT: 0x%x", hr)
// }
// return newICLRMetaHost(ppInterface), nil
//}
func GetICLRMetaHost() (metahost *ICLRMetaHost, err error) {
var pMetaHost uintptr
hr := CLRCreateInstance(&CLSID_CLRMetaHost, &IID_ICLRMetaHost, &pMetaHost)
err = checkOK(hr, "CLRCreateInstance")
if err != nil {
return
}
metahost = NewICLRMetaHost(pMetaHost)
return
}
//ICLRMetaHost Interface from metahost.h
// https://stackoverflow.com/questions/37781676/how-to-use-com-component-object-model-in-golang
@@ -49,7 +52,7 @@ type ICLRMetaHostVtbl struct {
}
// newICLRMetaHost takes a uintptr to ICLRMetahost, which must come from the syscall CLRCreateInstance
func newICLRMetaHost(ppv uintptr) *ICLRMetaHost {
func NewICLRMetaHost(ppv uintptr) *ICLRMetaHost {
return (*ICLRMetaHost)(unsafe.Pointer(ppv))
}
@@ -83,16 +86,16 @@ func (obj *ICLRMetaHost) EnumerateInstalledRuntimes(pInstalledRuntimes *uintptr)
return ret
}
func (obj *ICLRMetaHost) GetRuntime(pwzVersion *uint16, riid *guid.GUID, pRuntimeHost *uintptr) uintptr {
v4Ptr, err := syscall.UTF16PtrFromString("v4.0.30319")
if err != nil {
panic(err)
}
func (obj *ICLRMetaHost) GetRuntime(pwzVersion *uint16, riid *windows.GUID, pRuntimeHost *uintptr) uintptr {
// v4Ptr, err := syscall.UTF16PtrFromString("v4.0.30319")
// if err != nil {
// panic(err)
// }
ret, _, _ := syscall.Syscall6(
obj.vtbl.GetRuntime,
4,
uintptr(unsafe.Pointer(obj)),
uintptr(unsafe.Pointer(v4Ptr)),
uintptr(unsafe.Pointer(pwzVersion)),
uintptr(unsafe.Pointer(&IID_ICLRRuntimeInfo)),
uintptr(unsafe.Pointer(pRuntimeHost)),
0,
+5 -3
View File
@@ -1,4 +1,6 @@
package main
// +build windows
package clr
import (
"syscall"
@@ -24,7 +26,7 @@ type ICLRRuntimeHostVtbl struct {
ExecuteInDefaultAppDomain uintptr
}
func newICLRRuntimeHost(ppv uintptr) *ICLRRuntimeHost {
func NewICLRRuntimeHost(ppv uintptr) *ICLRRuntimeHost {
return (*ICLRRuntimeHost)(unsafe.Pointer(ppv))
}
@@ -74,7 +76,7 @@ func (obj *ICLRRuntimeHost) ExecuteInDefaultAppDomain(pwzAssemblyPath, pwzTypeNa
return ret
}
func (obj *ICLRRuntimeHost) GetCurrentappDomainID(pdwAppDomainId *uint16) uintptr {
func (obj *ICLRRuntimeHost) GetCurrentAppDomainID(pdwAppDomainId *uint16) uintptr {
ret, _, _ := syscall.Syscall(
obj.vtbl.GetCurrentAppDomainId,
2,
+6 -4
View File
@@ -1,7 +1,9 @@
package main
// +build windows
package clr
import (
"github.com/Microsoft/go-winio/pkg/guid"
"golang.org/x/sys/windows"
"syscall"
"unsafe"
)
@@ -28,7 +30,7 @@ type ICLRRuntimeInfoVtbl struct {
IsStarted uintptr
}
func newICLRRuntimeInfo(ppv uintptr) *ICLRRuntimeInfo {
func NewICLRRuntimeInfo(ppv uintptr) *ICLRRuntimeInfo {
return (*ICLRRuntimeInfo)(unsafe.Pointer(ppv))
}
@@ -62,7 +64,7 @@ func (obj *ICLRRuntimeInfo) GetVersionString(pcchBuffer *uint16, pVersionstringS
return ret
}
func (obj *ICLRRuntimeInfo) GetInterface(rclsid *guid.GUID, riid *guid.GUID, ppUnk *uintptr) uintptr {
func (obj *ICLRRuntimeInfo) GetInterface(rclsid *windows.GUID, riid *windows.GUID, ppUnk *uintptr) uintptr {
ret, _, _ := syscall.Syscall6(
obj.vtbl.GetInterface,
4,
+4 -2
View File
@@ -1,4 +1,6 @@
package main
// +build windows
package clr
import (
"syscall"
@@ -34,7 +36,7 @@ type ICORRuntimeHostVtbl struct {
CurrentDomain uintptr
}
func newICORRuntimeHost(ppv uintptr) *ICORRuntimeHost {
func NewICORRuntimeHost(ppv uintptr) *ICORRuntimeHost {
return (*ICORRuntimeHost)(unsafe.Pointer(ppv))
}
+4 -2
View File
@@ -1,4 +1,6 @@
package main
// +build windows
package clr
import (
"syscall"
@@ -19,7 +21,7 @@ type IEnumUnknownVtbl struct {
Clone uintptr
}
func newIEnumUnknown(ppv uintptr) *IEnumUnknown {
func NewIEnumUnknown(ppv uintptr) *IEnumUnknown {
return (*IEnumUnknown)(unsafe.Pointer(ppv))
}
+7 -4
View File
@@ -1,9 +1,12 @@
package main
// +build windows
package clr
import (
"github.com/Microsoft/go-winio/pkg/guid"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
type IUnknown struct {
@@ -16,11 +19,11 @@ type IUnknownVtbl struct {
Release uintptr
}
func newIUnknown(ppv uintptr) *IUnknown {
func NewIUnknown(ppv uintptr) *IUnknown {
return (*IUnknown)(unsafe.Pointer(ppv))
}
func (obj *IUnknown) QueryInterface(riid *guid.GUID, ppvObject *uintptr) uintptr {
func (obj *IUnknown) QueryInterface(riid *windows.GUID, ppvObject *uintptr) uintptr {
ret, _, _ := syscall.Syscall(
obj.vtbl.QueryInterface,
3,
+1 -273
View File
@@ -1,273 +1 @@
//+build windows
package main
import (
"fmt"
"github.com/Microsoft/go-winio/pkg/guid"
"io/ioutil"
"log"
"syscall"
"unsafe"
//ole "github.com/go-ole/go-ole"
//"github.com/go-ole/go-ole/oleutil"
)
const S_OK = 0
const E_POINTER = 0x80004003
var (
CLSID_CLRMetaHost = guid.GUID{0x9280188d, 0xe8e, 0x4867, [8]byte{0xb3, 0xc, 0x7f, 0xa8, 0x38, 0x84, 0xe8, 0xde}}
IID_ICLRMetaHost = guid.GUID{0xD332DB9E, 0xB9B3, 0x4125, [8]byte{0x82, 0x07, 0xA1, 0x48, 0x84, 0xF5, 0x32, 0x16}}
IID_ICLRRuntimeInfo = guid.GUID{0xBD39D1D2, 0xBA2F, 0x486a, [8]byte{0x89, 0xB0, 0xB4, 0xB0, 0xCB, 0x46, 0x68, 0x91}}
//EXTERN_GUID(CLSID_CLRRuntimeHost, 0x90F1A06E, 0x7712, 0x4762, 0x86, 0xB5, 0x7A, 0x5E, 0xBA, 0x6B, 0xDB, 0x02);
CLSID_CLRRuntimeHost = guid.GUID{0x90F1A06E, 0x7712, 0x4762, [8]byte{0x86, 0xB5, 0x7A, 0x5E, 0xBA, 0x6B, 0xDB, 0x02}}
//EXTERN_GUID(IID_ICLRRuntimeHost, 0x90F1A06C, 0x7712, 0x4762, 0x86, 0xB5, 0x7A, 0x5E, 0xBA, 0x6B, 0xDB, 0x02);
IID_ICLRRuntimeHost = guid.GUID{0x90F1A06C, 0x7712, 0x4762, [8]byte{0x86, 0xB5, 0x7A, 0x5E, 0xBA, 0x6B, 0xDB, 0x02}}
//EXTERN_GUID(IID_ICorRuntimeHost, 0xcb2f6722, 0xab3a, 0x11d2, 0x9c, 0x40, 0x00, 0xc0, 0x4f, 0xa3, 0x0a, 0x3e);
IID_ICorRuntimeHost = guid.GUID{0xcb2f6722, 0xab3a, 0x11d2, [8]byte{0x9c, 0x40, 0x00, 0xc0, 0x4f, 0xa3, 0x0a, 0x3e}}
//EXTERN_GUID(CLSID_CorRuntimeHost, 0xcb2f6723, 0xab3a, 0x11d2, 0x9c, 0x40, 0x00, 0xc0, 0x4f, 0xa3, 0x0a, 0x3e);
CLSID_CorRuntimeHost = guid.GUID{0xcb2f6723, 0xab3a, 0x11d2, [8]byte{0x9c, 0x40, 0x00, 0xc0, 0x4f, 0xa3, 0x0a, 0x3e}}
//https://docs.microsoft.com/en-us/dotnet/api/system._appdomain?view=netframework-4.8
// _AppDomain Interface GUID
IID_AppDomain = guid.GUID{0x5f696dc, 0x2b29, 0x3663, [8]uint8{0xad, 0x8b, 0xc4, 0x38, 0x9c, 0xf2, 0xa7, 0x13}}
)
func must(err error) {
if err != nil {
log.Fatal(err)
}
}
func checkOK(hr uintptr) {
if hr == S_OK {
return
} else if hr == E_POINTER {
log.Fatalf("Error getting pointer: 0x%x\n", hr)
} else {
log.Fatalf("Unknown return result: 0x%x\n", hr)
}
}
func main() {
var pMetaHost uintptr
var metaHost *ICLRMetaHost
hr := CLRCreateInstance(&CLSID_CLRMetaHost, &IID_ICLRMetaHost, &pMetaHost)
checkOK(hr)
metaHost = newICLRMetaHost(pMetaHost)
//metaHost, err := GetICLRMetaHost()
//must(err)
//var pInstalledRuntimes uintptr
//
//hr = metaHost.EnumerateInstalledRuntimes(&pInstalledRuntimes)
//
//checkOK(hr)
//installedRuntimes := newIEnumUnknown(pInstalledRuntimes)
//
//var pRuntimeInfo uintptr
//var fetched = uint32(0)
//var versionString string
//versionStringBytes := make([]uint16, 20)
//versionStringSize := uint32(20)
//var runtimeInfo *ICLRRuntimeInfo
//fmt.Println("[+] Enumerating installed runtimes...")
//for {
// hr = installedRuntimes.Next(1, &pRuntimeInfo, &fetched )
// if hr != S_OK {break}
// runtimeInfo = newICLRRuntimeInfo(pRuntimeInfo)
// if ret := runtimeInfo.GetVersionString(&versionStringBytes[0], &versionStringSize); ret != S_OK {
// log.Fatalf("[+] Error getting version string: 0x%x", ret)
// }
// versionString = syscall.UTF16ToString(versionStringBytes)
// fmt.Printf("\t[+] Found: %s\n", versionString)
//}
//fmt.Printf("[+] Using latest supported framework: %s\n", versionString)
//So I'll use the older API for now
var pRuntimeInfo uintptr
var runtimeInfo *ICLRRuntimeInfo
versionString := "v4.0.30319"
pwzVersion, _ := syscall.UTF16PtrFromString(versionString)
hr = metaHost.GetRuntime(pwzVersion, &IID_ICLRRuntimeInfo, &pRuntimeInfo)
checkOK(hr)
runtimeInfo = newICLRRuntimeInfo(pRuntimeInfo)
fmt.Println("[+] Got RuntimeHost")
var isLoadable bool
hr = runtimeInfo.IsLoadable(&isLoadable)
checkOK(hr)
fmt.Printf("[+] isLoadable: %t\n", isLoadable)
if !isLoadable {
log.Fatal("[!] IsLoadable returned false. Won't load CLR")
}
fmt.Println("[+] BindAsLegacyV2Runtime...")
hr = runtimeInfo.BindAsLegacyV2Runtime()
checkOK(hr)
var pRuntimeHost uintptr
// This is the "new" API, but I can't figure out how to load assemblies from memory in unmanaged code :/
//hr = runtimeInfo.GetInterface(&CLSID_CLRRuntimeHost, &IID_ICLRRuntimeHost, &pRuntimeHost)
// So I'll use the older API for now
//pwzVersion, _ := syscall.UTF16PtrFromString(versionString)
//hr = metaHost.GetRuntime(pwzVersion, &IID_ICLRRuntimeInfo, &pRuntimeInfo)
//checkOK(hr)
//runtimeInfo = newICLRRuntimeInfo(pRuntimeInfo)
fmt.Println("[+] Getting interface...")
hr = runtimeInfo.GetInterface(&CLSID_CorRuntimeHost, &IID_ICorRuntimeHost, &pRuntimeHost)
checkOK(hr)
runTimeHost := newICORRuntimeHost(pRuntimeHost)
fmt.Println("[+] Got interface. Loading CLR...")
hr = runTimeHost.Start()
checkOK(hr)
fmt.Println("[+] Loaded CLR into this process")
fmt.Println("[+] Getting Default AppDomain")
//ole.CoInitialize(0)
//var pIUnknown uintptr
//https://docs.microsoft.com/en-us/dotnet/api/system._appdomain?view=netframework-4.8
//unknown, err := oleutil.CreateObject("{05F696DC-2B29-3663-AD8B-C4389CF2A713}")
//if err != nil {
// fmt.Printf("createobject failed: %s", err)
//}
//must(err)
//iid, _ := oleutil.ClassIDFrom("{05F696DC-2B29-3663-AD8B-C4389CF2A713}")
//oleappdomain, _ := unknown.QueryInterface(ole.IID_IDispatch)
var pAppDomain uintptr
var pIUnknown uintptr
hr = runTimeHost.GetDefaultDomain(&pIUnknown)
checkOK(hr)
iu := newIUnknown(pIUnknown)
//appDomainGuid, err := guid.FromString("05F696DC-2B29-3663-AD8B-C4389CF2A713")
//must(err)
hr = iu.QueryInterface(&IID_AppDomain, &pAppDomain)
appDomain := newAppDomain(pAppDomain)
fmt.Println("[+] Got default appdomain")
//box := packr.New("EXEs", "./static")
//testEXEBytes, err := box.Find("TestEXE.exe")
//must(err)
testEXEBytes, err := ioutil.ReadFile("./static/TestEXE.exe")
must(err)
//testEXEBytes := []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41}
fmt.Printf("[+] Loaded %d bytes from TestExe.exe\n", len(testEXEBytes))
safeArray, err := createSafeArray(testEXEBytes)
must(err)
var pAssembly uintptr
hr = appDomain.Load_3(uintptr(unsafe.Pointer(&safeArray)), &pAssembly)
checkOK(hr)
fmt.Printf("[+] Assembly loaded into memory at 0x%08x\n", pAssembly)
assembly := newAssembly(pAssembly)
var pEntryPointInfo uintptr
fmt.Printf("entrypoint: 0x%08x\n", pEntryPointInfo)
hr = assembly.GetEntryPoint(&pEntryPointInfo)
checkOK(hr)
fmt.Printf("entrypoint: 0x%08x\n", pEntryPointInfo)
fmt.Println("[+] Found assembly entrypoint")
var pRetCode uintptr
nullVariant := Variant{
VT: 1,
Val: 0,
}
//printRawData(uintptr(unsafe.Pointer(&nullVariant)), unsafe.Sizeof(nullVariant))
methodInfo := newMethodInfo(pEntryPointInfo)
//methodGUID, err := guid.FromString("ffcc1b5d-ecb8-38dd-9b01-3dc8abc2aa5f")
//must(err)
//iu = newIUnknown(pEntryPointInfo)
//hr = iu.QueryInterface(&methodGUID, &pRetCode)
//checkOK(hr)
//fmt.Printf("[+] MethodInfo at 0x%08x\n", pRetCode)
////
////methodInfo := newMethodInfo(pRetCode)
//fmt.Println("testing gettype")
//hr = methodInfo.GetType(&pRetCode)
//checkOK(hr)
//
fmt.Println("[+] Calling default entry point with no args")
hr = methodInfo.Invoke_3(
uintptr(unsafe.Pointer(&nullVariant)),
uintptr(0),
&pRetCode)
checkOK(hr)
//var appDomainID uint16
//runTimeHost.GetCurrentappDomainID(&appDomainID)
//checkOK(hr)
//fmt.Printf("[+] Current AppDomain ID: %d\n", appDomainID)
//
//fmt.Println("[+] Executing assembly...")
//pDLLPath, _ := syscall.UTF16PtrFromString("TestDLL.dll")
//pTypeName, _ := syscall.UTF16PtrFromString("TestDLL.HelloWorld")
//pMethodName, _ := syscall.UTF16PtrFromString("SayHello")
//pArgument, _ := syscall.UTF16PtrFromString("foobar")
//var pReturnVal *uint16
//hr = runTimeHost.ExecuteInDefaultAppDomain(
// pDLLPath,
// pTypeName,
// pMethodName,
// pArgument,
// pReturnVal)
//
//checkOK(hr)
//fmt.Printf("[+] Assembly returned: 0x%x\n", pReturnVal)
//if hr == S_OK {
// log.Println("[+] Enumerating runtimes")
//} else if hr == E_POINTER {
// log.Fatal("[!] Couldn't get pointer to enumerate runtimes")
//} else {
// log.Fatalf("[!] Unknown error: %x\n", hr)
//}
//
//
//
//var pRuntimeInfo uintptr
//
//hr = metaHost.GetRuntime("v4.0.30319", pRuntimeInfo)
//if hr == S_OK {
// log.Println("[+] Got Runtime!")
//} else if hr == E_POINTER {
// log.Fatalf("[!] Error getting runtime pointer: %x\n", hr)
//} else {
// log.Fatalf("[!] Unknown error: %x\n", hr)
//}
}
func printRawData(ptr uintptr, size uintptr) {
fmt.Printf("Printing ptr %016x size %d\n", ptr, size)
i := ptr
var offset uintptr
for i < ptr+size {
if offset%16 == 0 {
fmt.Printf("%016x : ", i)
}
fmt.Printf("%02x", *(*byte)(unsafe.Pointer(i)))
i++
offset++
if offset%16 == 0 || offset == size {
fmt.Print("\n")
} else if offset%8 == 0 {
fmt.Print(" ")
} else {
fmt.Print(" ")
}
}
}
package clr
+9 -6
View File
@@ -1,9 +1,12 @@
package main
// +build windows
package clr
import (
"github.com/Microsoft/go-winio/pkg/guid"
"syscall"
"unsafe"
"github.com/Microsoft/go-winio/pkg/guid"
)
// from mscorlib.tlh
@@ -56,7 +59,7 @@ type MethodInfoVtbl struct {
GetBaseDefinition uintptr
}
func newMethodInfo(ppv uintptr) *MethodInfo {
func NewMethodInfo(ppv uintptr) *MethodInfo {
return (*MethodInfo)(unsafe.Pointer(ppv))
}
@@ -100,16 +103,16 @@ func (obj *MethodInfo) GetType(pRetVal *uintptr) uintptr {
return ret
}
func (obj *MethodInfo) Invoke_3(variantObj, parameters uintptr, pRetVal *uintptr) uintptr {
func (obj *MethodInfo) Invoke_3(variantObj Variant, parameters uintptr, pRetVal *uintptr) uintptr {
ret, _, _ := syscall.Syscall6(
obj.vtbl.Invoke_3,
4,
uintptr(unsafe.Pointer(obj)),
uintptr(unsafe.Pointer(variantObj)),
uintptr(unsafe.Pointer(&variantObj)),
0,
uintptr(unsafe.Pointer(pRetVal)),
0,
0,
)
)
return ret
}
+6 -7
View File
@@ -1,7 +1,10 @@
package main
// +build windows
package clr
import (
"fmt"
"runtime"
"syscall"
"unsafe"
)
@@ -15,8 +18,6 @@ import (
// from OAld.h
type SafeArray struct {
cDims uint16
fFeatures uint16
@@ -31,7 +32,7 @@ type SafeArrayBound struct {
lLbound int32
}
func createSafeArray(rawBytes []byte) (SafeArray, error) {
func CreateSafeArray(rawBytes []byte) (SafeArray, error) {
modOleAuto, err := syscall.LoadDLL("OleAut32.dll")
if err != nil {
return SafeArray{}, err
@@ -57,6 +58,7 @@ func createSafeArray(rawBytes []byte) (SafeArray, error) {
}
sa := (*SafeArray)(unsafe.Pointer(ret))
runtime.KeepAlive(sa)
// now we need to use RtlCopyMemory to copy our bytes to the SafeArray
modNtDll, err := syscall.LoadDLL("ntdll.dll")
if err != nil {
@@ -75,9 +77,6 @@ func createSafeArray(rawBytes []byte) (SafeArray, error) {
return SafeArray{}, err
}
//printRawData(uintptr(unsafe.Pointer(&sa)), uintptr(32))
return *sa, nil
//return uintptr(unsafe.Pointer(&sa)), nil
}
+24
View File
@@ -0,0 +1,24 @@
// +build windows
package clr
import (
"fmt"
"log"
)
const S_OK = 0x0
func checkOK(hr uintptr, caller string) error {
if hr != S_OK {
return fmt.Errorf("%s returned 0x%08x", caller, hr)
} else {
return nil
}
}
func must(err error) {
if err != nil {
log.Fatal(err)
}
}
+5 -3
View File
@@ -1,4 +1,6 @@
package main
// +build windows
package clr
import "unsafe"
@@ -9,10 +11,10 @@ type Variant struct {
wReserved1 uint16
wReserved2 uint16
wReserved3 uint16
Val int64
Val uintptr
_ [8]byte
}
func newVariant(ppv uintptr) *Variant {
func variantFromPtr(ppv uintptr) *Variant {
return (*Variant)(unsafe.Pointer(ppv))
}