add support for params in memory execution and stablize load_3

This commit is contained in:
ayoul3
2020-08-02 12:08:39 +02:00
parent 23e1d165e1
commit 0cc5ebba45
8 changed files with 163 additions and 54 deletions
+2 -1
View File
@@ -3,9 +3,10 @@
package clr
import (
"golang.org/x/sys/windows"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
type AppDomain struct {
+7 -7
View File
@@ -3,15 +3,16 @@
package main
import (
clr "github.com/ropnop/go-clr"
"log"
"fmt"
"io/ioutil"
"log"
"runtime"
clr "github.com/ropnop/go-clr"
)
func main() {
fmt.Println("[+] Loading DLL from Disk")
/*fmt.Println("[+] Loading DLL from Disk")
ret, err := clr.ExecuteDLLFromDisk(
"TestDLL.dll",
"TestDLL.HelloWorld",
@@ -21,16 +22,15 @@ func main() {
log.Fatal(err)
}
fmt.Printf("[+] DLL Return Code: %d\n", ret)
*/
fmt.Println("[+] Executing EXE from memory")
exebytes, err := ioutil.ReadFile("helloworld.exe")
exebytes, err := ioutil.ReadFile(`C:\Users\ayoul3\Documents\go\reflect-pe\res\managed.exe`)
if err != nil {
log.Fatal(err)
}
runtime.KeepAlive(exebytes)
ret2, err := clr.ExecuteByteArray(exebytes)
ret2, err := clr.ExecuteByteArray(exebytes, []string{"test", "test2"})
if err != nil {
log.Fatal(err)
}
+45 -6
View File
@@ -6,7 +6,6 @@ package clr
import (
"fmt"
"runtime"
"strings"
"syscall"
"unsafe"
@@ -108,7 +107,7 @@ func ExecuteDLLFromDisk(dllpath, typeName, methodName, argument string) (retCode
// ExecuteByteArray is a wrapper function that will automatically load the latest supported framework into the current
// process using the legacy APIs, then load and execute an executable from memory. It takes in a byte array of the
// executable to load and run and returns the return code. It currently does not support any arguments on the entry point
func ExecuteByteArray(rawBytes []byte) (retCode int32, err error) {
func ExecuteByteArray(rawBytes []byte, params []string) (retCode int32, err error) {
retCode = -1
metahost, err := GetICLRMetaHost()
if err != nil {
@@ -149,13 +148,12 @@ func ExecuteByteArray(rawBytes []byte) (retCode int32, err error) {
if err != nil {
return
}
safeArray, err := CreateSafeArray(rawBytes)
safeArrayPtr, err := CreateSafeArray(rawBytes)
if err != nil {
return
}
runtime.KeepAlive(&safeArray)
var pAssembly uintptr
hr = appDomain.Load_3(uintptr(unsafe.Pointer(&safeArray)), &pAssembly)
hr = appDomain.Load_3(uintptr(safeArrayPtr), &pAssembly)
err = checkOK(hr, "appDomain.Load_3")
if err != nil {
return
@@ -168,6 +166,21 @@ func ExecuteByteArray(rawBytes []byte) (retCode int32, err error) {
return
}
methodInfo := NewMethodInfoFromPtr(pEntryPointInfo)
var methodSignaturePtr, paramPtr uintptr
err = methodInfo.GetString(&methodSignaturePtr)
if err != nil {
return
}
methodSignature := readUnicodeStr(unsafe.Pointer(methodSignaturePtr))
if expectsParams(methodSignature) {
paramPtr, err = PrepareParameters(params)
if err != nil {
return -1, err
}
}
var pRetCode uintptr
nullVariant := Variant{
VT: 1,
@@ -175,7 +188,7 @@ func ExecuteByteArray(rawBytes []byte) (retCode int32, err error) {
}
hr = methodInfo.Invoke_3(
nullVariant,
uintptr(0),
paramPtr,
&pRetCode)
err = checkOK(hr, "methodInfo.Invoke_3")
if err != nil {
@@ -188,3 +201,29 @@ func ExecuteByteArray(rawBytes []byte) (retCode int32, err error) {
return int32(pRetCode), nil
}
func PrepareParameters(params []string) (uintptr, error) {
listStrSafeArrayPtr, err := CreateEmptySafeArray(0x0008, len(params)) // VT_BSTR
if err != nil {
return 0, err
}
for i, p := range params {
bstr, _ := SysAllocString(p)
SafeArrayPutElement(listStrSafeArrayPtr, bstr, i)
}
paramVariant := Variant{
VT: 0x0008 | 0x2000, // VT_BSTR | VT_ARRAY
Val: uintptr(listStrSafeArrayPtr),
}
paramsSafeArrayPtr, err := CreateEmptySafeArray(0x000C, 1) // VT_VARIANT
if err != nil {
return 0, err
}
err = SafeArrayPutElement(paramsSafeArrayPtr, unsafe.Pointer(&paramVariant), 0)
if err != nil {
return 0, err
}
return uintptr(paramsSafeArrayPtr), nil
}
+4 -1
View File
@@ -2,4 +2,7 @@ module github.com/ropnop/go-clr
go 1.13
require golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527
require (
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527
golang.org/x/text v0.3.3
)
+3
View File
@@ -1,2 +1,5 @@
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=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+14 -2
View File
@@ -3,9 +3,10 @@
package clr
import (
"golang.org/x/sys/windows"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
// from mscorlib.tlh
@@ -108,10 +109,21 @@ func (obj *MethodInfo) Invoke_3(variantObj Variant, parameters uintptr, pRetVal
4,
uintptr(unsafe.Pointer(obj)),
uintptr(unsafe.Pointer(&variantObj)),
0,
parameters,
uintptr(unsafe.Pointer(pRetVal)),
0,
0,
)
return ret
}
func (obj *MethodInfo) GetString(addr *uintptr) error {
ret, _, _ := syscall.Syscall(
obj.vtbl.get_ToString,
2,
uintptr(unsafe.Pointer(obj)),
uintptr(unsafe.Pointer(addr)),
0,
)
return checkOK(ret, "get_ToString")
}
+52 -34
View File
@@ -3,8 +3,6 @@
package clr
import (
"fmt"
"runtime"
"syscall"
"unsafe"
)
@@ -34,52 +32,72 @@ type SafeArrayBound struct {
// CreateSafeArray is a wrapper function that takes in a Go byte array and creates a SafeArray by making two syscalls
// and copying raw memory into the correct spot.
func CreateSafeArray(rawBytes []byte) (SafeArray, error) {
modOleAuto, err := syscall.LoadDLL("OleAut32.dll")
func CreateSafeArray(rawBytes []byte) (unsafe.Pointer, error) {
saPtr, err := CreateEmptySafeArray(0x11, len(rawBytes)) // VT_UI1
if err != nil {
return SafeArray{}, err
return nil, err
}
procSafeArrayCreate, err := modOleAuto.FindProc("SafeArrayCreate")
if err != nil {
return SafeArray{}, err
// now we need to use RtlCopyMemory to copy our bytes to the SafeArray
modNtDll := syscall.MustLoadDLL("ntdll.dll")
procRtlCopyMemory := modNtDll.MustFindProc("RtlCopyMemory")
sa := (*SafeArray)(saPtr)
_, _, err = procRtlCopyMemory.Call(
sa.pvData,
uintptr(unsafe.Pointer(&rawBytes[0])),
uintptr(len(rawBytes)))
if err != syscall.Errno(0) {
return nil, err
}
return saPtr, nil
}
size := len(rawBytes)
func CreateEmptySafeArray(arrayType int, size int) (unsafe.Pointer, error) {
modOleAuto := syscall.MustLoadDLL("OleAut32.dll")
procSafeArrayCreate := modOleAuto.MustFindProc("SafeArrayCreate")
sab := SafeArrayBound{
cElements: uint32(size),
lLbound: 0,
}
runtime.KeepAlive(sab)
vt := uint16(0x11) // VT_UI1
ret, _, _ := procSafeArrayCreate.Call(
vt := uint16(arrayType)
ret, _, err := procSafeArrayCreate.Call(
uintptr(vt),
uintptr(1),
uintptr(unsafe.Pointer(&sab)))
if ret == 0 {
return SafeArray{}, fmt.Errorf("Error creating SafeArray")
}
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 {
return SafeArray{}, err
}
procRtlCopyMemory, err := modNtDll.FindProc("RtlCopyMemory")
if err != nil {
return SafeArray{}, err
}
ret, _, err = procRtlCopyMemory.Call(
sa.pvData,
uintptr(unsafe.Pointer(&rawBytes[0])),
uintptr(size))
if err != syscall.Errno(0) {
return SafeArray{}, err
return nil, err
}
return *sa, nil
return unsafe.Pointer(ret), nil
}
func SysAllocString(str string) (unsafe.Pointer, error) {
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
}
return unsafe.Pointer(ret), nil
}
func SafeArrayPutElement(array, btsr unsafe.Pointer, index int) (err error) {
modOleAuto := syscall.MustLoadDLL("OleAut32.dll")
safeArrayPutElement := modOleAuto.MustFindProc("SafeArrayPutElement")
_, _, err = safeArrayPutElement.Call(
uintptr(array),
uintptr(unsafe.Pointer(&index)),
uintptr(btsr),
)
if err != syscall.Errno(0) {
return err
}
return nil
}
+33
View File
@@ -3,8 +3,15 @@
package clr
import (
"bytes"
"fmt"
"log"
"strings"
"unicode/utf16"
"unsafe"
"golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
)
const S_OK = 0x0
@@ -22,3 +29,29 @@ func must(err error) {
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()")
}
func readUnicodeStr(ptr unsafe.Pointer) string {
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))
}