mirror of
https://github.com/ropnop/go-clr
synced 2026-06-08 17:11:59 +00:00
add support for params in memory execution and stablize load_3
This commit is contained in:
+2
-1
@@ -3,9 +3,10 @@
|
||||
package clr
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type AppDomain struct {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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(¶mVariant), 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uintptr(paramsSafeArrayPtr), nil
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
@@ -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")
|
||||
}
|
||||
|
||||
+55
-37
@@ -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")
|
||||
if err != nil {
|
||||
return SafeArray{}, err
|
||||
}
|
||||
procSafeArrayCreate, err := modOleAuto.FindProc("SafeArrayCreate")
|
||||
if err != nil {
|
||||
return SafeArray{}, err
|
||||
}
|
||||
func CreateSafeArray(rawBytes []byte) (unsafe.Pointer, error) {
|
||||
|
||||
saPtr, err := CreateEmptySafeArray(0x11, len(rawBytes)) // VT_UI1
|
||||
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")
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
func CreateEmptySafeArray(arrayType int, size int) (unsafe.Pointer, error) {
|
||||
modOleAuto := syscall.MustLoadDLL("OleAut32.dll")
|
||||
procSafeArrayCreate := modOleAuto.MustFindProc("SafeArrayCreate")
|
||||
|
||||
size := len(rawBytes)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user