139 lines
3.0 KiB
Go
139 lines
3.0 KiB
Go
//go:build windows
|
|||
|
|
|
||
|
|
package plugins
|
||
|
|
|
||
|
|
import (
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
"sync"
|
||
|
|
"syscall"
|
||
|
|
"unsafe"
|
||
|
|
)
|
||
|
|
|
||
|
|
func loadNativePlugin(data []byte) (NativePlugin, error) {
|
||
|
|
if len(data) == 0 {
|
||
|
|
return nil, errors.New("empty plugin binary")
|
||
|
|
}
|
||
|
|
|
||
|
|
mm, err := LoadMemoryModule(data)
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("pe load: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := mm.CallEntryPoint(dllProcessAttach); err != nil {
|
||
|
|
mm.Free()
|
||
|
|
return nil, fmt.Errorf("DllMain init: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
onLoad, err := mm.GetExport("PluginOnLoad")
|
||
|
|
if err != nil {
|
||
|
|
mm.Free()
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
onEvent, err := mm.GetExport("PluginOnEvent")
|
||
|
|
if err != nil {
|
||
|
|
mm.Free()
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
onUnload, err := mm.GetExport("PluginOnUnload")
|
||
|
|
if err != nil {
|
||
|
|
mm.Free()
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
setCallback, _ := mm.GetExport("PluginSetCallback")
|
||
|
|
|
||
|
|
dp := &dllPlugin{
|
||
|
|
mem: mm,
|
||
|
|
onLoadAddr: onLoad,
|
||
|
|
onEventAddr: onEvent,
|
||
|
|
onUnloadAddr: onUnload,
|
||
|
|
setCallbackAddr: setCallback,
|
||
|
|
}
|
||
|
|
return dp, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
type dllPlugin struct {
|
||
|
|
mem *MemoryModule
|
||
|
|
onLoadAddr uintptr
|
||
|
|
onEventAddr uintptr
|
||
|
|
onUnloadAddr uintptr
|
||
|
|
setCallbackAddr uintptr
|
||
|
|
callbackHandle uintptr // prevent GC of the callback closure
|
||
|
|
mu sync.Mutex
|
||
|
|
}
|
||
|
|
|
||
|
|
func (p *dllPlugin) Load(send func(string, []byte), hostInfo []byte) error {
|
||
|
|
p.mu.Lock()
|
||
|
|
defer p.mu.Unlock()
|
||
|
|
|
||
|
|
// Create a stdcall callback the DLL can invoke to send events to the host.
|
||
|
|
cb := syscall.NewCallback(func(eventPtr, eventLen, payloadPtr, payloadLen uintptr) uintptr {
|
||
|
|
event := make([]byte, eventLen)
|
||
|
|
if eventLen > 0 {
|
||
|
|
copy(event, unsafe.Slice((*byte)(unsafe.Pointer(eventPtr)), eventLen))
|
||
|
|
}
|
||
|
|
payload := make([]byte, payloadLen)
|
||
|
|
if payloadLen > 0 {
|
||
|
|
copy(payload, unsafe.Slice((*byte)(unsafe.Pointer(payloadPtr)), payloadLen))
|
||
|
|
}
|
||
|
|
send(string(event), payload)
|
||
|
|
return 0
|
||
|
|
})
|
||
|
|
p.callbackHandle = cb
|
||
|
|
|
||
|
|
if p.setCallbackAddr != 0 {
|
||
|
|
syscall.SyscallN(p.setCallbackAddr, cb)
|
||
|
|
}
|
||
|
|
|
||
|
|
var infoPtr uintptr
|
||
|
|
infoLen := uintptr(len(hostInfo))
|
||
|
|
if len(hostInfo) > 0 {
|
||
|
|
infoPtr = uintptr(unsafe.Pointer(&hostInfo[0]))
|
||
|
|
}
|
||
|
|
ret, _, _ := syscall.SyscallN(p.onLoadAddr, infoPtr, infoLen, cb)
|
||
|
|
if int32(ret) != 0 {
|
||
|
|
return errors.New("PluginOnLoad returned non-zero")
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (p *dllPlugin) Event(event string, payload []byte) error {
|
||
|
|
p.mu.Lock()
|
||
|
|
defer p.mu.Unlock()
|
||
|
|
|
||
|
|
eventBytes := []byte(event)
|
||
|
|
var eventPtr, payloadPtr uintptr
|
||
|
|
eventLen := uintptr(len(eventBytes))
|
||
|
|
payloadLen := uintptr(len(payload))
|
||
|
|
if len(eventBytes) > 0 {
|
||
|
|
eventPtr = uintptr(unsafe.Pointer(&eventBytes[0]))
|
||
|
|
}
|
||
|
|
if len(payload) > 0 {
|
||
|
|
payloadPtr = uintptr(unsafe.Pointer(&payload[0]))
|
||
|
|
}
|
||
|
|
|
||
|
|
ret, _, _ := syscall.SyscallN(p.onEventAddr, eventPtr, eventLen, payloadPtr, payloadLen)
|
||
|
|
if int32(ret) != 0 {
|
||
|
|
return errors.New("PluginOnEvent returned non-zero")
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (p *dllPlugin) Unload() {
|
||
|
|
p.mu.Lock()
|
||
|
|
defer p.mu.Unlock()
|
||
|
|
if p.onUnloadAddr != 0 {
|
||
|
|
syscall.SyscallN(p.onUnloadAddr)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (p *dllPlugin) Close() error {
|
||
|
|
p.Unload()
|
||
|
|
if p.mem != nil {
|
||
|
|
p.mem.Free()
|
||
|
|
p.mem = nil
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|