Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C2 Framework Support #8

Open
wants to merge 34 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b99b1aa
Merge pull request #1 from ropnop/master
Ne0nd0g Feb 26, 2021
86c796b
Update SafeArray functions, fixed example program
Ne0nd0g Mar 18, 2021
46d27d5
Fixed composite literal use of unkeyed fields
Ne0nd0g Mar 18, 2021
d54978b
Fixed misuse of unsafe.Pointer in iclrmetahost
Ne0nd0g Mar 18, 2021
b3af3fa
Fixed misuse of unsafe.Pointer for iclrmetahost
Ne0nd0g Mar 18, 2021
d17a78a
Modified iclrruntimeinfo methods
Ne0nd0g Mar 18, 2021
4715ed5
Fixed misuse of unsafe.Pointer
Ne0nd0g Mar 19, 2021
e81f8eb
Update GetDefaultDomain
Ne0nd0g Mar 19, 2021
a46d3ab
Fixed misuse of unsafe.Pointer
Ne0nd0g Mar 19, 2021
f0888b8
Fixed misuse of unsafe.Pointer
Ne0nd0g Mar 19, 2021
a88b841
Fixed misuse of unsafe.Pointer
Ne0nd0g Mar 19, 2021
07d499f
Fixed misuse of unsafe.Pointer
Ne0nd0g Mar 19, 2021
ff8ffa7
Updated MethodInfo and PrepareParameters
Ne0nd0g Mar 19, 2021
e8b430e
Updated runtime enumeration
Ne0nd0g Mar 19, 2021
fed3a5e
Fixed misuse of unsafe.Pointer
Ne0nd0g Mar 19, 2021
278992d
Fixed misuse of unsafe.Pointer
Ne0nd0g Mar 19, 2021
d5949ed
Fixed misuse of unsafe.Pointer
Ne0nd0g Mar 19, 2021
441da9d
Code cleanup
Ne0nd0g Mar 19, 2021
3c6feff
Documentation and cleanup
Ne0nd0g Mar 19, 2021
85f36cd
Added C2 Framework Support
Ne0nd0g Mar 25, 2021
d791ef9
Updated STDOUT/STDERR Read
Ne0nd0g Mar 27, 2021
46c71d5
Added SafeArrayDelete
Ne0nd0g Mar 27, 2021
b7ac0af
Removed unused code
Ne0nd0g Mar 27, 2021
a2bef24
Update go.mod
Ne0nd0g Mar 28, 2021
875ea40
Updated STDOUT/STDERR to use a buffer
Ne0nd0g Apr 8, 2021
37fb9d6
Modules + Tags Hard...
Ne0nd0g Apr 8, 2021
b14c9ea
ignore windows error that doesn't impact execution
audibleblink Feb 10, 2022
474a510
Merge pull request #2 from audibleblink/patch/edgcase_error
Ne0nd0g Apr 2, 2022
b96d0f1
Updated supporting packages
Ne0nd0g Apr 2, 2022
d57278d
Remove must from go-clr.go
mec07 Oct 25, 2022
cb41266
Fixed #4
Ne0nd0g Nov 10, 2022
ce145f5
Merge pull request #3 from mec07/remove-must
Ne0nd0g Nov 10, 2022
7aa95c6
v1.3.0
Ne0nd0g Nov 10, 2022
6176f4a
Fixed CHANGELOG version numbers
Ne0nd0g Nov 10, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Changelog
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## 1.0.3 2022-11-10

## Changed

- Merged [Pull 3](https://github.com/Ne0nd0g/go-clr/pull/3) from @mec07 to return errors instead of exiting the program

### Fixed

- [Issue 4](https://github.com/Ne0nd0g/go-clr/issues/4) - Application's Without A Console Will Not Return Output

## 1.0.2 2022-04-02

### Fixed

- Merged [Pull 2](https://github.com/Ne0nd0g/go-clr/pull/2) from @audibleblink that fixed an error when attempting to
load correctly targeted assemblies through

### Changed

- Upgraded supporting Go modules

## 1.0.1 2021-04-08

- Learned how to correctly use tags and updated import

## 1.0.0 2021-04-08

- Initial tagged version from forked

### Added

- Added buffer to collect redirected STDOUT/STDERR
100 changes: 78 additions & 22 deletions appdomain.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
// +build windows
//go:build windows

package clr

import (
"fmt"
"syscall"
"unsafe"

"golang.org/x/sys/windows"
)

// AppDomain is a Windows COM object interface pointer for the .NET AppDomain class.
// The AppDomain object represents an application domain, which is an isolated environment where applications execute.
// This structure only contains a pointer to the AppDomain's virtual function table
// https://docs.microsoft.com/en-us/dotnet/api/system.appdomain?view=netframework-4.8
type AppDomain struct {
vtbl *AppDomainVtbl
}

// AppDomainVtbl is a Virtual Function Table for the AppDomain COM interface
// The Virtual Function Table contains pointers to the COM IUnkown interface
// functions (QueryInterface, AddRef, & Release) as well as the AppDomain object's methods
// https://docs.microsoft.com/en-us/dotnet/api/system.appdomain?view=netframework-4.8
type AppDomainVtbl struct {
QueryInterface uintptr
AddRef uintptr
Expand Down Expand Up @@ -88,24 +97,17 @@ type AppDomainVtbl struct {

// GetAppDomain is a wrapper function that returns an appDomain from an existing ICORRuntimeHost object
func GetAppDomain(runtimeHost *ICORRuntimeHost) (appDomain *AppDomain, err error) {
var pAppDomain uintptr
var pIUnknown uintptr
hr := runtimeHost.GetDefaultDomain(&pIUnknown)
err = checkOK(hr, "runtimeHost.GetDefaultDomain")
debugPrint("Entering into appdomain.GetAppDomain()...")
iu, err := runtimeHost.GetDefaultDomain()
if err != nil {
return
}
iu := NewIUnknownFromPtr(pIUnknown)
hr = iu.QueryInterface(&IID_AppDomain, &pAppDomain)
err = checkOK(hr, "IUnknown.QueryInterface")
return NewAppDomainFromPtr(pAppDomain), err
}

func NewAppDomainFromPtr(ppv uintptr) *AppDomain {
return (*AppDomain)(unsafe.Pointer(ppv))
err = iu.QueryInterface(IID_AppDomain, unsafe.Pointer(&appDomain))
return
}

func (obj *AppDomain) QueryInterface(riid *windows.GUID, ppvObject *uintptr) uintptr {
debugPrint("Entering into appdomain.QueryInterface()...")
ret, _, _ := syscall.Syscall(
obj.vtbl.QueryInterface,
3,
Expand Down Expand Up @@ -135,22 +137,76 @@ func (obj *AppDomain) Release() uintptr {
return ret
}

func (obj *AppDomain) GetHashCode() uintptr {
ret, _, _ := syscall.Syscall(
// GetHashCode serves as the default hash function.
// https://docs.microsoft.com/en-us/dotnet/api/system.object.gethashcode?view=netframework-4.8#System_Object_GetHashCode
func (obj *AppDomain) GetHashCode() (int32, error) {
debugPrint("Entering into appdomain.GetHashCode()...")
ret, _, err := syscall.Syscall(
obj.vtbl.GetHashCode,
2,
uintptr(unsafe.Pointer(obj)),
0,
0)
return ret
0,
)
if err != syscall.Errno(0) {
return 0, fmt.Errorf("the appdomain.GetHashCode function returned an error:\r\n%s", err)
}
// Unable to avoid misuse of unsafe.Pointer because the Windows API call returns the safeArray pointer in the "ret" value. This is a go vet false positive
return int32(ret), nil
}

func (obj *AppDomain) Load_3(pRawAssembly uintptr, asmbly *uintptr) uintptr {
ret, _, _ := syscall.Syscall(
// Load_3 Loads an Assembly into this application domain.
// virtual HRESULT __stdcall Load_3 (
// /*[in]*/ SAFEARRAY * rawAssembly,
// /*[out,retval]*/ struct _Assembly * * pRetVal ) = 0;
// https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.load?view=net-5.0
func (obj *AppDomain) Load_3(rawAssembly *SafeArray) (assembly *Assembly, err error) {
debugPrint("Entering into appdomain.Load_3()...")
hr, _, err := syscall.Syscall(
obj.vtbl.Load_3,
3,
uintptr(unsafe.Pointer(obj)),
uintptr(unsafe.Pointer(pRawAssembly)),
uintptr(unsafe.Pointer(asmbly)))
return ret
uintptr(unsafe.Pointer(rawAssembly)),
uintptr(unsafe.Pointer(&assembly)),
)

if err != syscall.Errno(0) {
if err != syscall.Errno(1150) {
return
}
}

if hr != S_OK {
err = fmt.Errorf("the appdomain.Load_3 function returned a non-zero HRESULT: 0x%x", hr)
return
}
err = nil

return
}

// ToString Obtains a string representation that includes the friendly name of the application domain and any context policies.
// https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.tostring?view=net-5.0#System_AppDomain_ToString
func (obj *AppDomain) ToString() (domain string, err error) {
debugPrint("Entering into appdomain.ToString()...")
var pDomain *string
hr, _, err := syscall.Syscall(
obj.vtbl.get_ToString,
2,
uintptr(unsafe.Pointer(obj)),
uintptr(unsafe.Pointer(&pDomain)),
0,
)

if err != syscall.Errno(0) {
err = fmt.Errorf("the AppDomain.ToString method retured an error:\r\n%s", err)
return
}
if hr != S_OK {
err = fmt.Errorf("the AppDomain.ToString method returned a non-zero HRESULT: 0x%x", hr)
return
}
err = nil
domain = ReadUnicodeStr(unsafe.Pointer(pDomain))
return
}
36 changes: 26 additions & 10 deletions assembly.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
package clr

import (
"golang.org/x/sys/windows"
"fmt"
"syscall"
"unsafe"

"golang.org/x/sys/windows"
)

// from mscorlib.tlh
Expand All @@ -14,6 +16,8 @@ type Assembly struct {
vtbl *AssemblyVtbl
}

// AssemblyVtbl is a COM virtual table of functions for the Assembly Class
// https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assembly?view=netframework-4.8
type AssemblyVtbl struct {
QueryInterface uintptr
AddRef uintptr
Expand Down Expand Up @@ -68,10 +72,6 @@ type AssemblyVtbl struct {
get_GlobalAssemblyCache uintptr
}

func NewAssemblyFromPtr(ppv uintptr) *Assembly {
return (*Assembly)(unsafe.Pointer(ppv))
}

func (obj *Assembly) QueryInterface(riid *windows.GUID, ppvObject *uintptr) uintptr {
ret, _, _ := syscall.Syscall(
obj.vtbl.QueryInterface,
Expand Down Expand Up @@ -102,12 +102,28 @@ func (obj *Assembly) Release() uintptr {
return ret
}

func (obj *Assembly) GetEntryPoint(pMethodInfo *uintptr) uintptr {
ret, _, _ := syscall.Syscall(
// GetEntryPoint returns the assembly's MethodInfo
// virtual HRESULT __stdcall get_EntryPoint (
// /*[out,retval]*/ struct _MethodInfo * * pRetVal ) = 0;
// https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assembly.entrypoint?view=netframework-4.8#System_Reflection_Assembly_EntryPoint
// https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodinfo?view=netframework-4.8
func (obj *Assembly) GetEntryPoint() (pRetVal *MethodInfo, err error) {
debugPrint("Entering into assembly.GetEntryPoint()...")
hr, _, err := syscall.Syscall(
obj.vtbl.get_EntryPoint,
2,
uintptr(unsafe.Pointer(obj)),
uintptr(unsafe.Pointer(pMethodInfo)),
0)
return ret
uintptr(unsafe.Pointer(&pRetVal)),
0,
)
if err != syscall.Errno(0) {
err = fmt.Errorf("the Assembly::GetEntryPoint method returned an error:\r\n%s", err)
return
}
if hr != S_OK {
err = fmt.Errorf("the Assembly::GetEntryPoint method returned a non-zero HRESULT: 0x%x", hr)
return
}
err = nil
return
}
Loading