mirror of
https://github.com/yv1ing/ShotRDP.git
synced 2025-09-16 15:10:57 +08:00
基本适应win7,win10,win server 08,win server 12,win server 16的截图
This commit is contained in:
328
grdp/plugin/addins.go
Normal file
328
grdp/plugin/addins.go
Normal file
@@ -0,0 +1,328 @@
|
||||
// +build ignore
|
||||
|
||||
// addins.go
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
openHandleSeq uint32
|
||||
freerdpClient2 = syscall.NewLazyDLL("freerdp-client2.dll")
|
||||
winpr2 = syscall.NewLazyDLL("winpr2.dll")
|
||||
freerdp2 = syscall.NewLazyDLL("freerdp2.dll")
|
||||
)
|
||||
|
||||
var (
|
||||
virtualChannelEntry = freerdpClient2.NewProc("VirtualChannelEntry")
|
||||
virtualChannelEntryEx = freerdpClient2.NewProc("VirtualChannelEntryEx")
|
||||
)
|
||||
|
||||
func VirtualChannelEntryEx(ex *ChannelEntryPointsEx, pInitHandle interface{}) (err error) {
|
||||
r0, _, ec := virtualChannelEntryEx.Call(uintptr(unsafe.Pointer(ex)),
|
||||
uintptr(unsafe.Pointer(&pInitHandle)))
|
||||
if r0 == 0 {
|
||||
err = error(ec)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ChannelsClientLoadEx(cs *rdpChannels) {
|
||||
var client ChannelClientData
|
||||
client.entryEx = VIRTUALCHANNELENTRYEX(VirtualChannelEntryEx)
|
||||
cs.clientDataList = append(cs.clientDataList, client)
|
||||
cs.clientDataCount++
|
||||
|
||||
var init ChannelInitData
|
||||
init.channels = cs
|
||||
init.openDataMap = make(map[uint32]*ChannelOpenData)
|
||||
cs.initDataList = append(cs.initDataList, init)
|
||||
cs.initDataCount++
|
||||
|
||||
ex := NewChannelEntryPointsEx()
|
||||
ex.PVirtualChannelInitEx = VIRTUALCHANNELINITEX(RdpVirtualChannelInitEx)
|
||||
ex.PVirtualChannelOpenEx = VIRTUALCHANNELOPENEX(RdpVirtualChannelOpenEx)
|
||||
ex.PVirtualChannelCloseEx = VIRTUALCHANNELCLOSEEX(RdpVirtualChannelCloseEx)
|
||||
ex.PVirtualChannelWriteEx = VIRTUALCHANNELWRITEEX(RdpVirtualChannelWriteEx)
|
||||
client.entryEx(ex, uintptr(unsafe.Pointer(&init)))
|
||||
}
|
||||
|
||||
type ChannelClientData struct {
|
||||
//PVIRTUALCHANNELENTRY entry;
|
||||
entryEx VIRTUALCHANNELENTRYEX
|
||||
// pChannelInitEventProc *CHANNEL_INIT_EVENT_FN
|
||||
pChannelInitEventProcEx CHANNEL_INIT_EVENT_EX_FN
|
||||
pInitHandle interface{}
|
||||
lpUserParam interface{}
|
||||
}
|
||||
type ChannelOpenData struct {
|
||||
name string
|
||||
OpenHandle uint32
|
||||
options uint32
|
||||
flags int
|
||||
pInterface interface{}
|
||||
channels *rdpChannels
|
||||
lpUserParam interface{}
|
||||
//pChannelOpenEventProc *CHANNEL_OPEN_EVENT_FN
|
||||
pChannelOpenEventProcEx *CHANNEL_OPEN_EVENT_EX_FN
|
||||
}
|
||||
type ChannelInitData struct {
|
||||
channels *rdpChannels
|
||||
pInterface interface{}
|
||||
openDataMap map[uint32]*ChannelOpenData
|
||||
}
|
||||
type rdpChannels struct {
|
||||
clientDataCount int
|
||||
clientDataList []ChannelClientData
|
||||
|
||||
openDataCount int
|
||||
openDataList []ChannelOpenData
|
||||
|
||||
initDataCount int
|
||||
initDataList []ChannelInitData
|
||||
|
||||
/* control for entry into MyVirtualChannelInit */
|
||||
can_call_init bool
|
||||
|
||||
/* true once freerdp_channels_post_connect is called */
|
||||
connected bool
|
||||
|
||||
/* used for locating the channels for a given instance */
|
||||
//freerdp* instance;
|
||||
|
||||
//wMessageQueue* queue;
|
||||
|
||||
//DrdynvcClientContext* drdynvc;
|
||||
//CRITICAL_SECTION channelsLock;
|
||||
}
|
||||
type ChannelOpenEvent struct {
|
||||
Data interface{}
|
||||
DataLength uint32
|
||||
UserData interface{}
|
||||
pChannelOpenData *ChannelOpenData
|
||||
}
|
||||
|
||||
func RdpVirtualChannelInitEx(lpUserParam interface{}, clientContext interface{},
|
||||
pInitHandle interface{}, pChannel []ChannelDef,
|
||||
channelCount int, versionRequested uint32,
|
||||
pChannelInitEventProcEx CHANNEL_INIT_EVENT_EX_FN) uint {
|
||||
var (
|
||||
//rdpSettings* settings;
|
||||
pChannelInitData *ChannelInitData
|
||||
pChannelClientData *ChannelClientData
|
||||
channels *rdpChannels
|
||||
)
|
||||
|
||||
if pInitHandle == nil {
|
||||
return CHANNEL_RC_BAD_INIT_HANDLE
|
||||
}
|
||||
|
||||
if pChannel == nil {
|
||||
return CHANNEL_RC_BAD_CHANNEL
|
||||
}
|
||||
|
||||
if (channelCount <= 0) || pChannelInitEventProcEx == nil {
|
||||
return CHANNEL_RC_INITIALIZATION_ERROR
|
||||
}
|
||||
|
||||
pChannelInitData = pInitHandle.(*ChannelInitData)
|
||||
//WINPR_ASSERT(pChannelInitData);
|
||||
|
||||
channels = pChannelInitData.channels
|
||||
//WINPR_ASSERT(channels);
|
||||
|
||||
if !channels.can_call_init {
|
||||
return CHANNEL_RC_NOT_IN_VIRTUALCHANNELENTRY
|
||||
}
|
||||
|
||||
if (channels.openDataCount + channelCount) > 30 {
|
||||
return CHANNEL_RC_TOO_MANY_CHANNELS
|
||||
}
|
||||
|
||||
if channels.connected {
|
||||
return CHANNEL_RC_ALREADY_CONNECTED
|
||||
}
|
||||
|
||||
if versionRequested != VIRTUAL_CHANNEL_VERSION_WIN2000 {
|
||||
}
|
||||
|
||||
for i := range pChannel {
|
||||
pChannelDef := &pChannel[i]
|
||||
if getChannelOpenDataByName(channels, pChannelDef.Name) == nil {
|
||||
return CHANNEL_RC_BAD_CHANNEL
|
||||
}
|
||||
}
|
||||
|
||||
pChannelClientData = &channels.clientDataList[channels.clientDataCount]
|
||||
pChannelClientData.pChannelInitEventProcEx = pChannelInitEventProcEx
|
||||
pChannelClientData.pInitHandle = pInitHandle
|
||||
pChannelClientData.lpUserParam = lpUserParam
|
||||
channels.clientDataCount++
|
||||
|
||||
//WINPR_ASSERT(channels->instance);
|
||||
//WINPR_ASSERT(channels->instance->context);
|
||||
//settings = channels.instance.context.settings
|
||||
//WINPR_ASSERT(settings);
|
||||
|
||||
for i := range pChannel {
|
||||
pChannelDef := &pChannel[i]
|
||||
var pChannelOpenData ChannelOpenData
|
||||
|
||||
//WINPR_ASSERT(pChannelOpenData)
|
||||
|
||||
pChannelOpenData.OpenHandle = atomic.AddUint32(&openHandleSeq, 1)
|
||||
pChannelOpenData.channels = channels
|
||||
pChannelOpenData.lpUserParam = lpUserParam
|
||||
if _, ok := pChannelInitData.openDataMap[pChannelOpenData.OpenHandle]; ok {
|
||||
return CHANNEL_RC_INITIALIZATION_ERROR
|
||||
}
|
||||
|
||||
pChannelInitData.pInterface = clientContext
|
||||
|
||||
pChannelOpenData.flags = 1
|
||||
pChannelOpenData.name = pChannelDef.Name
|
||||
pChannelOpenData.options = pChannelDef.Options
|
||||
pChannelInitData.openDataMap[pChannelOpenData.OpenHandle] = &pChannelOpenData
|
||||
channels.openDataList = append(channels.openDataList, pChannelOpenData)
|
||||
channels.openDataCount++
|
||||
/*
|
||||
if settings.ChannelCount < 30 {
|
||||
channel := freerdp_settings_get_pointer_array_writable(
|
||||
settings, FreeRDP_ChannelDefArray, settings.ChannelCount)
|
||||
channel.name = pChannelDef.Name
|
||||
channel.options = pChannelDef.Options
|
||||
settings.ChannelCount++
|
||||
}*/
|
||||
|
||||
channels.openDataCount++
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK
|
||||
}
|
||||
func getChannelOpenDataByName(channel *rdpChannels, name string) *ChannelOpenData {
|
||||
for _, v := range channel.openDataList {
|
||||
if strings.EqualFold(name, v.name) {
|
||||
return &v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func RdpVirtualChannelOpenEx(pInitHandle interface{}, pOpenHandle *uint32, pChannelName string,
|
||||
pChannelOpenEventProcEx *CHANNEL_OPEN_EVENT_EX_FN) uint {
|
||||
pChannelInitData := pInitHandle.(*ChannelInitData)
|
||||
channels := pChannelInitData.channels
|
||||
pInterface := pChannelInitData.pInterface
|
||||
|
||||
if pOpenHandle == nil {
|
||||
return CHANNEL_RC_BAD_CHANNEL_HANDLE
|
||||
}
|
||||
if pChannelOpenEventProcEx == nil {
|
||||
return CHANNEL_RC_BAD_PROC
|
||||
}
|
||||
|
||||
if !channels.connected {
|
||||
return CHANNEL_RC_NOT_CONNECTED
|
||||
}
|
||||
|
||||
pChannelOpenData := getChannelOpenDataByName(channels, pChannelName)
|
||||
|
||||
if pChannelOpenData == nil {
|
||||
return CHANNEL_RC_UNKNOWN_CHANNEL_NAME
|
||||
}
|
||||
|
||||
if pChannelOpenData.flags == 2 {
|
||||
return CHANNEL_RC_ALREADY_OPEN
|
||||
}
|
||||
|
||||
pChannelOpenData.flags = 2 /* open */
|
||||
pChannelOpenData.pInterface = pInterface
|
||||
pChannelOpenData.pChannelOpenEventProcEx = pChannelOpenEventProcEx
|
||||
*pOpenHandle = pChannelOpenData.OpenHandle
|
||||
return CHANNEL_RC_OK
|
||||
}
|
||||
func RdpVirtualChannelCloseEx(pInitHandle interface{}, openHandle uint32) uint {
|
||||
if pInitHandle == nil {
|
||||
return CHANNEL_RC_BAD_INIT_HANDLE
|
||||
}
|
||||
pChannelInitData := pInitHandle.(*ChannelInitData)
|
||||
pChannelOpenData := pChannelInitData.openDataMap[openHandle]
|
||||
|
||||
if pChannelOpenData == nil {
|
||||
return CHANNEL_RC_BAD_CHANNEL_HANDLE
|
||||
}
|
||||
|
||||
if pChannelOpenData.flags != 2 {
|
||||
return CHANNEL_RC_NOT_OPEN
|
||||
}
|
||||
|
||||
pChannelOpenData.flags = 0
|
||||
|
||||
return CHANNEL_RC_OK
|
||||
}
|
||||
func RdpVirtualChannelWriteEx(pInitHandle interface{}, openHandle uint32,
|
||||
pData interface{}, dataLength uint32,
|
||||
pUserData interface{}) uint {
|
||||
|
||||
//wMessage message;
|
||||
|
||||
if pInitHandle == nil {
|
||||
return CHANNEL_RC_BAD_INIT_HANDLE
|
||||
}
|
||||
|
||||
pChannelInitData := pInitHandle.(*ChannelInitData)
|
||||
channels := pChannelInitData.channels
|
||||
|
||||
if channels == nil {
|
||||
return CHANNEL_RC_BAD_CHANNEL_HANDLE
|
||||
}
|
||||
|
||||
pChannelOpenData := pChannelInitData.openDataMap[openHandle]
|
||||
if pChannelOpenData == nil {
|
||||
return CHANNEL_RC_BAD_CHANNEL_HANDLE
|
||||
}
|
||||
|
||||
if !channels.connected {
|
||||
return CHANNEL_RC_NOT_CONNECTED
|
||||
}
|
||||
|
||||
if pData == nil {
|
||||
return CHANNEL_RC_NULL_DATA
|
||||
}
|
||||
|
||||
if dataLength == 0 {
|
||||
return CHANNEL_RC_ZERO_LENGTH
|
||||
}
|
||||
|
||||
if pChannelOpenData.flags != 2 {
|
||||
return CHANNEL_RC_NOT_OPEN
|
||||
}
|
||||
|
||||
pChannelOpenEvent := new(ChannelOpenEvent)
|
||||
|
||||
if pChannelOpenEvent == nil {
|
||||
return CHANNEL_RC_NO_MEMORY
|
||||
|
||||
}
|
||||
|
||||
pChannelOpenEvent.Data = pData
|
||||
pChannelOpenEvent.DataLength = dataLength
|
||||
pChannelOpenEvent.UserData = pUserData
|
||||
pChannelOpenEvent.pChannelOpenData = pChannelOpenData
|
||||
/*message.context = channels;
|
||||
message.id = 0;
|
||||
message.wParam = pChannelOpenEvent;
|
||||
message.lParam = NULL;
|
||||
message.Free = channel_queue_message_free;
|
||||
|
||||
if (!MessageQueue_Dispatch(channels->queue, &message))
|
||||
{
|
||||
free(pChannelOpenEvent);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}*/
|
||||
|
||||
return CHANNEL_RC_OK
|
||||
}
|
||||
304
grdp/plugin/channel.go
Normal file
304
grdp/plugin/channel.go
Normal file
@@ -0,0 +1,304 @@
|
||||
package plugin
|
||||
|
||||
import "C"
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"ShotRDP/grdp/glog"
|
||||
|
||||
"ShotRDP/grdp/core"
|
||||
"ShotRDP/grdp/emission"
|
||||
)
|
||||
|
||||
const (
|
||||
CHANNEL_RC_OK = 0
|
||||
CHANNEL_RC_ALREADY_INITIALIZED = 1
|
||||
CHANNEL_RC_NOT_INITIALIZED = 2
|
||||
CHANNEL_RC_ALREADY_CONNECTED = 3
|
||||
CHANNEL_RC_NOT_CONNECTED = 4
|
||||
CHANNEL_RC_TOO_MANY_CHANNELS = 5
|
||||
CHANNEL_RC_BAD_CHANNEL = 6
|
||||
CHANNEL_RC_BAD_CHANNEL_HANDLE = 7
|
||||
CHANNEL_RC_NO_BUFFER = 8
|
||||
CHANNEL_RC_BAD_INIT_HANDLE = 9
|
||||
CHANNEL_RC_NOT_OPEN = 10
|
||||
CHANNEL_RC_BAD_PROC = 11
|
||||
CHANNEL_RC_NO_MEMORY = 12
|
||||
CHANNEL_RC_UNKNOWN_CHANNEL_NAME = 13
|
||||
CHANNEL_RC_ALREADY_OPEN = 14
|
||||
CHANNEL_RC_NOT_IN_VIRTUALCHANNELENTRY = 15
|
||||
CHANNEL_RC_NULL_DATA = 16
|
||||
CHANNEL_RC_ZERO_LENGTH = 17
|
||||
CHANNEL_RC_INVALID_INSTANCE = 18
|
||||
CHANNEL_RC_UNSUPPORTED_VERSION = 19
|
||||
CHANNEL_RC_INITIALIZATION_ERROR = 20
|
||||
)
|
||||
const (
|
||||
VIRTUAL_CHANNEL_VERSION_WIN2000 = 1
|
||||
)
|
||||
|
||||
const (
|
||||
CHANNEL_EVENT_INITIALIZED = 0
|
||||
CHANNEL_EVENT_CONNECTED = 1
|
||||
CHANNEL_EVENT_V1_CONNECTED = 2
|
||||
CHANNEL_EVENT_DISCONNECTED = 3
|
||||
CHANNEL_EVENT_TERMINATED = 4
|
||||
CHANNEL_EVENT_REMOTE_CONTROL_START = 5
|
||||
CHANNEL_EVENT_REMOTE_CONTROL_STOP = 6
|
||||
CHANNEL_EVENT_ATTACHED = 7
|
||||
CHANNEL_EVENT_DETACHED = 8
|
||||
CHANNEL_EVENT_DATA_RECEIVED = 10
|
||||
CHANNEL_EVENT_WRITE_COMPLETE = 11
|
||||
CHANNEL_EVENT_WRITE_CANCELLED = 12
|
||||
)
|
||||
|
||||
const (
|
||||
CHANNEL_OPTION_INITIALIZED = 0x80000000
|
||||
CHANNEL_OPTION_ENCRYPT_RDP = 0x40000000
|
||||
CHANNEL_OPTION_ENCRYPT_SC = 0x20000000
|
||||
CHANNEL_OPTION_ENCRYPT_CS = 0x10000000
|
||||
CHANNEL_OPTION_PRI_HIGH = 0x08000000
|
||||
CHANNEL_OPTION_PRI_MED = 0x04000000
|
||||
CHANNEL_OPTION_PRI_LOW = 0x02000000
|
||||
CHANNEL_OPTION_COMPRESS_RDP = 0x00800000
|
||||
CHANNEL_OPTION_COMPRESS = 0x00400000
|
||||
CHANNEL_OPTION_SHOW_PROTOCOL = 0x00200000
|
||||
CHANNEL_OPTION_REMOTE_CONTROL_PERSISTENT = 0x00100000
|
||||
)
|
||||
|
||||
type ChannelDef struct {
|
||||
Name string
|
||||
Options uint32
|
||||
}
|
||||
type CHANNEL_INIT_EVENT_EX_FN func(lpUserParam interface{},
|
||||
pInitHandle interface{}, event uint, pData uintptr, dataLength uint)
|
||||
type VIRTUALCHANNELINITEX func(lpUserParam interface{}, clientContext interface{},
|
||||
pInitHandle interface{}, pChannel []ChannelDef,
|
||||
channelCount int, versionRequested uint32,
|
||||
pChannelInitEventProcEx CHANNEL_INIT_EVENT_EX_FN) uint
|
||||
|
||||
type CHANNEL_OPEN_EVENT_EX_FN func(lpUserParam uintptr,
|
||||
openHandle uint32, event uint,
|
||||
pData uintptr, dataLength uint32, totalLength uint32, dataFlags uint32)
|
||||
type VIRTUALCHANNELOPENEX func(pInitHandle interface{}, pOpenHandle *uint32,
|
||||
pChannelName string,
|
||||
pChannelOpenEventProcEx *CHANNEL_OPEN_EVENT_EX_FN) uint
|
||||
|
||||
type VIRTUALCHANNELCLOSEEX func(pInitHandle interface{}, openHandle uint32) uint
|
||||
|
||||
type VIRTUALCHANNELWRITEEX func(pInitHandle interface{}, openHandle uint32, pData interface{},
|
||||
dataLength uint32, pUserData interface{}) uint
|
||||
|
||||
type ChannelEntryPointsEx struct {
|
||||
CbSize uint32
|
||||
ProtocolVersion uint32
|
||||
PVirtualChannelInitEx VIRTUALCHANNELINITEX
|
||||
PVirtualChannelOpenEx VIRTUALCHANNELOPENEX
|
||||
PVirtualChannelCloseEx VIRTUALCHANNELCLOSEEX
|
||||
PVirtualChannelWriteEx VIRTUALCHANNELWRITEEX
|
||||
}
|
||||
|
||||
func NewChannelEntryPointsEx() *ChannelEntryPointsEx {
|
||||
e := &ChannelEntryPointsEx{}
|
||||
e.CbSize = uint32(unsafe.Sizeof(e))
|
||||
e.ProtocolVersion = VIRTUAL_CHANNEL_VERSION_WIN2000
|
||||
return e
|
||||
}
|
||||
|
||||
type VIRTUALCHANNELENTRYEX func(pEntryPointsEx *ChannelEntryPointsEx,
|
||||
pInitHandle interface{}) error
|
||||
|
||||
/*
|
||||
type ChannelEntryPoints struct {
|
||||
CbSize uint32
|
||||
ProtocolVersion uint32
|
||||
PVirtualChannelInit PVIRTUALCHANNELINIT
|
||||
PVirtualChannelOpen PVIRTUALCHANNELOPEN
|
||||
PVirtualChannelClose PVIRTUALCHANNELCLOSE
|
||||
PVirtualChannelWrite PVIRTUALCHANNELWRITE
|
||||
}
|
||||
typedef VOID VCAPITYPE CHANNEL_INIT_EVENT_FN(LPVOID pInitHandle,
|
||||
UINT event, LPVOID pData, UINT dataLength);
|
||||
|
||||
typedef CHANNEL_INIT_EVENT_FN* PCHANNEL_INIT_EVENT_FN;
|
||||
typedef VOID VCAPITYPE CHANNEL_OPEN_EVENT_FN(DWORD openHandle, UINT event,
|
||||
LPVOID pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags);
|
||||
|
||||
typedef CHANNEL_OPEN_EVENT_FN* PCHANNEL_OPEN_EVENT_FN;
|
||||
typedef UINT VCAPITYPE VIRTUALCHANNELINIT(LPVOID* ppInitHandle, PCHANNEL_DEF pChannel,
|
||||
INT channelCount, ULONG versionRequested,
|
||||
PCHANNEL_INIT_EVENT_FN pChannelInitEventProc);
|
||||
typedef VIRTUALCHANNELINIT* PVIRTUALCHANNELINIT;
|
||||
|
||||
typedef UINT VCAPITYPE VIRTUALCHANNELOPEN(LPVOID pInitHandle, LPDWORD pOpenHandle,
|
||||
PCHAR pChannelName,
|
||||
PCHANNEL_OPEN_EVENT_FN pChannelOpenEventProc);
|
||||
|
||||
typedef VIRTUALCHANNELOPEN* PVIRTUALCHANNELOPEN;
|
||||
|
||||
typedef UINT VCAPITYPE VIRTUALCHANNELCLOSE(DWORD openHandle);
|
||||
typedef VIRTUALCHANNELCLOSE* PVIRTUALCHANNELCLOSE;
|
||||
|
||||
typedef UINT VCAPITYPE VIRTUALCHANNELWRITE(DWORD openHandle, LPVOID pData, ULONG dataLength,
|
||||
LPVOID pUserData);
|
||||
typedef VIRTUALCHANNELWRITE* PVIRTUALCHANNELWRITE;
|
||||
|
||||
typedef UINT VCAPITYPE VIRTUALCHANNELINITEX(LPVOID lpUserParam, LPVOID clientContext,
|
||||
LPVOID pInitHandle, PCHANNEL_DEF pChannel,
|
||||
INT channelCount, ULONG versionRequested,
|
||||
PCHANNEL_INIT_EVENT_EX_FN pChannelInitEventProcEx);
|
||||
typedef VIRTUALCHANNELINITEX* PVIRTUALCHANNELINITEX;
|
||||
|
||||
typedef UINT VCAPITYPE VIRTUALCHANNELOPENEX(LPVOID pInitHandle, LPDWORD pOpenHandle,
|
||||
PCHAR pChannelName,
|
||||
PCHANNEL_OPEN_EVENT_EX_FN pChannelOpenEventProcEx);
|
||||
typedef VIRTUALCHANNELOPENEX* PVIRTUALCHANNELOPENEX;
|
||||
|
||||
|
||||
typedef UINT VCAPITYPE VIRTUALCHANNELCLOSEEX(LPVOID pInitHandle, DWORD openHandle);
|
||||
typedef VIRTUALCHANNELCLOSEEX* PVIRTUALCHANNELCLOSEEX;
|
||||
|
||||
typedef UINT VCAPITYPE VIRTUALCHANNELWRITEEX(LPVOID pInitHandle, DWORD openHandle, LPVOID pData,
|
||||
ULONG dataLength, LPVOID pUserData);
|
||||
typedef VIRTUALCHANNELWRITEEX* PVIRTUALCHANNELWRITEEX;
|
||||
*/
|
||||
|
||||
// static channel name
|
||||
const (
|
||||
CLIPRDR_SVC_CHANNEL_NAME = "cliprdr" //剪切板
|
||||
RDPDR_SVC_CHANNEL_NAME = "rdpdr" //设备重定向(打印机,磁盘,端口,智能卡等)
|
||||
RDPSND_SVC_CHANNEL_NAME = "rdpsnd" //音频输出
|
||||
RAIL_SVC_CHANNEL_NAME = "rail" //远程应用
|
||||
DRDYNVC_SVC_CHANNEL_NAME = "drdynvc" //动态虚拟通道
|
||||
REMDESK_SVC_CHANNEL_NAME = "remdesk" //远程协助
|
||||
)
|
||||
|
||||
const (
|
||||
RDPGFX_DVC_CHANNEL_NAME = "Microsoft::Windows::RDS::Graphics" //图形扩展
|
||||
)
|
||||
|
||||
var StaticVirtualChannels = map[string]int{
|
||||
CLIPRDR_SVC_CHANNEL_NAME: CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
|
||||
CHANNEL_OPTION_COMPRESS_RDP | CHANNEL_OPTION_SHOW_PROTOCOL,
|
||||
RDPDR_SVC_CHANNEL_NAME: CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP,
|
||||
RDPSND_SVC_CHANNEL_NAME: CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
|
||||
CHANNEL_OPTION_COMPRESS_RDP | CHANNEL_OPTION_SHOW_PROTOCOL,
|
||||
RAIL_SVC_CHANNEL_NAME: CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
|
||||
CHANNEL_OPTION_COMPRESS_RDP | CHANNEL_OPTION_SHOW_PROTOCOL,
|
||||
}
|
||||
|
||||
const (
|
||||
CHANNEL_CHUNK_LENGTH = 1600
|
||||
CHANNEL_FLAG_FIRST = 0x01
|
||||
CHANNEL_FLAG_LAST = 0x02
|
||||
CHANNEL_FLAG_SHOW_PROTOCOL = 0x10
|
||||
)
|
||||
|
||||
type ChannelTransport interface {
|
||||
GetType() (string, uint32)
|
||||
Sender(core.ChannelSender)
|
||||
Process(s []byte)
|
||||
}
|
||||
type ChannelClient struct {
|
||||
ChannelDef
|
||||
t ChannelTransport
|
||||
}
|
||||
|
||||
type Channels struct {
|
||||
emission.Emitter
|
||||
channels map[string]ChannelClient
|
||||
transport core.Transport
|
||||
buff *bytes.Buffer
|
||||
channelSender core.ChannelSender
|
||||
}
|
||||
|
||||
func NewChannels(t core.Transport) *Channels {
|
||||
c := &Channels{
|
||||
Emitter: *emission.NewEmitter(),
|
||||
channels: make(map[string]ChannelClient, 20),
|
||||
transport: t,
|
||||
buff: &bytes.Buffer{},
|
||||
}
|
||||
t.On("channel", c.process)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Channels) SetChannelSender(f core.ChannelSender) {
|
||||
c.channelSender = f
|
||||
}
|
||||
func (c *Channels) Register(t ChannelTransport) {
|
||||
name, option := t.GetType()
|
||||
_, ok := c.channels[name]
|
||||
if ok {
|
||||
glog.Warn("Already register channel:", name)
|
||||
return
|
||||
}
|
||||
t.Sender(c)
|
||||
c.channels[name] = ChannelClient{ChannelDef{name, option}, t}
|
||||
}
|
||||
|
||||
func (c *Channels) SendToChannel(channel string, s []byte) (int, error) {
|
||||
cli, ok := c.channels[channel]
|
||||
if !ok {
|
||||
glog.Warn("No register channel:", channel)
|
||||
return 0, fmt.Errorf("No register channel: %s", channel)
|
||||
}
|
||||
idx := 0
|
||||
ln := len(s)
|
||||
b := &bytes.Buffer{}
|
||||
for ln > 0 {
|
||||
var flag uint32 = 0
|
||||
if cli.Options&CHANNEL_OPTION_SHOW_PROTOCOL != 0 {
|
||||
flag |= CHANNEL_FLAG_SHOW_PROTOCOL
|
||||
}
|
||||
if idx == 0 {
|
||||
flag |= CHANNEL_FLAG_FIRST
|
||||
}
|
||||
|
||||
var ss []byte
|
||||
if ln > CHANNEL_CHUNK_LENGTH {
|
||||
ss = s[idx : idx+CHANNEL_CHUNK_LENGTH]
|
||||
idx += CHANNEL_CHUNK_LENGTH
|
||||
} else {
|
||||
flag |= CHANNEL_FLAG_LAST
|
||||
ss = s[idx : idx+ln]
|
||||
}
|
||||
glog.Debug("len:", len(ss), "flag:", flag)
|
||||
ln -= len(ss)
|
||||
b.Reset()
|
||||
core.WriteUInt32LE(uint32(len(s)), b)
|
||||
core.WriteUInt32LE(flag, b)
|
||||
b.Write(ss)
|
||||
c.channelSender.SendToChannel(channel, b.Bytes())
|
||||
}
|
||||
return ln, nil
|
||||
}
|
||||
|
||||
func (c *Channels) process(channel string, s []byte) {
|
||||
cli, ok := c.channels[channel]
|
||||
if !ok {
|
||||
glog.Warn("No found channel:", channel)
|
||||
return
|
||||
}
|
||||
r := bytes.NewReader(s)
|
||||
ln, _ := core.ReadUInt32LE(r)
|
||||
flags, _ := core.ReadUInt32LE(r)
|
||||
glog.Debugf("channel:%s length: %d, flags: %d", channel, ln, flags)
|
||||
if flags&CHANNEL_FLAG_FIRST == 0 || flags&CHANNEL_FLAG_LAST == 0 {
|
||||
if flags&CHANNEL_FLAG_FIRST != 0 {
|
||||
c.buff.Reset()
|
||||
}
|
||||
b, _ := core.ReadBytes(r.Len(), r)
|
||||
c.buff.Write(b)
|
||||
if flags&CHANNEL_FLAG_LAST == 0 {
|
||||
return
|
||||
}
|
||||
s = c.buff.Bytes()
|
||||
} else {
|
||||
s, _ = core.ReadBytes(r.Len(), r)
|
||||
}
|
||||
|
||||
cli.t.Process(s)
|
||||
}
|
||||
771
grdp/plugin/cliprdr/cliprdr.go
Normal file
771
grdp/plugin/cliprdr/cliprdr.go
Normal file
@@ -0,0 +1,771 @@
|
||||
package cliprdr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"os"
|
||||
"strings"
|
||||
"unicode/utf16"
|
||||
|
||||
"github.com/lunixbochs/struc"
|
||||
|
||||
"ShotRDP/grdp/core"
|
||||
"ShotRDP/grdp/glog"
|
||||
"ShotRDP/grdp/plugin"
|
||||
)
|
||||
|
||||
/**
|
||||
* Initialization Sequence\n
|
||||
* Client Server\n
|
||||
* | |\n
|
||||
* |<----------------------Server Clipboard Capabilities PDU-----------------|\n
|
||||
* |<-----------------------------Monitor Ready PDU--------------------------|\n
|
||||
* |-----------------------Client Clipboard Capabilities PDU---------------->|\n
|
||||
* |---------------------------Temporary Directory PDU---------------------->|\n
|
||||
* |-------------------------------Format List PDU-------------------------->|\n
|
||||
* |<--------------------------Format List Response PDU----------------------|\n
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Data Transfer Sequences\n
|
||||
* Shared Local\n
|
||||
* Clipboard Owner Clipboard Owner\n
|
||||
* | |\n
|
||||
* |-------------------------------------------------------------------------|\n _
|
||||
* |-------------------------------Format List PDU-------------------------->|\n |
|
||||
* |<--------------------------Format List Response PDU----------------------|\n _| Copy
|
||||
* Sequence
|
||||
* |<---------------------Lock Clipboard Data PDU (Optional)-----------------|\n
|
||||
* |-------------------------------------------------------------------------|\n
|
||||
* |-------------------------------------------------------------------------|\n _
|
||||
* |<--------------------------Format Data Request PDU-----------------------|\n | Paste
|
||||
* Sequence Palette,
|
||||
* |---------------------------Format Data Response PDU--------------------->|\n _| Metafile,
|
||||
* File List Data
|
||||
* |-------------------------------------------------------------------------|\n
|
||||
* |-------------------------------------------------------------------------|\n _
|
||||
* |<------------------------Format Contents Request PDU---------------------|\n | Paste
|
||||
* Sequence
|
||||
* |-------------------------Format Contents Response PDU------------------->|\n _| File
|
||||
* Stream Data
|
||||
* |<---------------------Lock Clipboard Data PDU (Optional)-----------------|\n
|
||||
* |-------------------------------------------------------------------------|\n
|
||||
*
|
||||
*/
|
||||
|
||||
const (
|
||||
ChannelName = plugin.CLIPRDR_SVC_CHANNEL_NAME
|
||||
ChannelOption = plugin.CHANNEL_OPTION_INITIALIZED | plugin.CHANNEL_OPTION_ENCRYPT_RDP |
|
||||
plugin.CHANNEL_OPTION_COMPRESS_RDP | plugin.CHANNEL_OPTION_SHOW_PROTOCOL
|
||||
)
|
||||
|
||||
type MsgType uint16
|
||||
|
||||
const (
|
||||
CB_MONITOR_READY = 0x0001
|
||||
CB_FORMAT_LIST = 0x0002
|
||||
CB_FORMAT_LIST_RESPONSE = 0x0003
|
||||
CB_FORMAT_DATA_REQUEST = 0x0004
|
||||
CB_FORMAT_DATA_RESPONSE = 0x0005
|
||||
CB_TEMP_DIRECTORY = 0x0006
|
||||
CB_CLIP_CAPS = 0x0007
|
||||
CB_FILECONTENTS_REQUEST = 0x0008
|
||||
CB_FILECONTENTS_RESPONSE = 0x0009
|
||||
CB_LOCK_CLIPDATA = 0x000A
|
||||
CB_UNLOCK_CLIPDATA = 0x000B
|
||||
)
|
||||
|
||||
type MsgFlags uint16
|
||||
|
||||
const (
|
||||
CB_RESPONSE_OK = 0x0001
|
||||
CB_RESPONSE_FAIL = 0x0002
|
||||
CB_ASCII_NAMES = 0x0004
|
||||
)
|
||||
|
||||
type DwFlags uint32
|
||||
|
||||
const (
|
||||
FILECONTENTS_SIZE = 0x00000001
|
||||
FILECONTENTS_RANGE = 0x00000002
|
||||
)
|
||||
|
||||
type CliprdrPDUHeader struct {
|
||||
MsgType uint16 `struc:"little"`
|
||||
MsgFlags uint16 `struc:"little"`
|
||||
DataLen uint32 `struc:"little"`
|
||||
}
|
||||
|
||||
func NewCliprdrPDUHeader(mType, flags uint16, ln uint32) *CliprdrPDUHeader {
|
||||
return &CliprdrPDUHeader{
|
||||
MsgType: mType,
|
||||
MsgFlags: flags,
|
||||
DataLen: ln,
|
||||
}
|
||||
}
|
||||
func (h *CliprdrPDUHeader) serialize() []byte {
|
||||
b := &bytes.Buffer{}
|
||||
core.WriteUInt16LE(h.MsgType, b)
|
||||
core.WriteUInt16LE(h.MsgFlags, b)
|
||||
core.WriteUInt32LE(h.DataLen, b)
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
type CliprdrGeneralCapabilitySet struct {
|
||||
CapabilitySetType uint16 `struc:"little"`
|
||||
CapabilitySetLength uint16 `struc:"little"`
|
||||
Version uint32 `struc:"little"`
|
||||
GeneralFlags uint32 `struc:"little"`
|
||||
}
|
||||
|
||||
const (
|
||||
CB_CAPSTYPE_GENERAL = 0x0001
|
||||
)
|
||||
|
||||
type CliprdrCapabilitySets struct {
|
||||
CapabilitySetType uint16 `struc:"little"`
|
||||
LengthCapability uint16 `struc:"little"`
|
||||
Version uint32 `struc:"little"`
|
||||
GeneralFlags uint32 `struc:"little"`
|
||||
//CapabilityData []byte `struc:"little"`
|
||||
}
|
||||
type CliprdrCapabilitiesPDU struct {
|
||||
CCapabilitiesSets uint16 `struc:"little,sizeof=CapabilitySets"`
|
||||
Pad1 uint16 `struc:"little"`
|
||||
CapabilitySets []CliprdrGeneralCapabilitySet `struc:"little"`
|
||||
}
|
||||
|
||||
type CliprdrMonitorReady struct {
|
||||
}
|
||||
|
||||
type GeneralFlags uint32
|
||||
|
||||
const (
|
||||
/* CLIPRDR_GENERAL_CAPABILITY.generalFlags */
|
||||
CB_USE_LONG_FORMAT_NAMES = 0x00000002
|
||||
CB_STREAM_FILECLIP_ENABLED = 0x00000004
|
||||
CB_FILECLIP_NO_FILE_PATHS = 0x00000008
|
||||
CB_CAN_LOCK_CLIPDATA = 0x00000010
|
||||
CB_HUGE_FILE_SUPPORT_ENABLED = 0x00000020
|
||||
)
|
||||
|
||||
const (
|
||||
/* CLIPRDR_GENERAL_CAPABILITY.version */
|
||||
CB_CAPS_VERSION_1 = 0x00000001
|
||||
CB_CAPS_VERSION_2 = 0x00000002
|
||||
)
|
||||
const (
|
||||
CB_CAPSTYPE_GENERAL_LEN = 12
|
||||
)
|
||||
|
||||
const (
|
||||
FD_CLSID = 0x00000001
|
||||
FD_SIZEPOINT = 0x00000002
|
||||
FD_ATTRIBUTES = 0x00000004
|
||||
FD_CREATETIME = 0x00000008
|
||||
FD_ACCESSTIME = 0x00000010
|
||||
FD_WRITESTIME = 0x00000020
|
||||
FD_FILESIZE = 0x00000040
|
||||
FD_PROGRESSUI = 0x00004000
|
||||
FD_LINKUI = 0x00008000
|
||||
)
|
||||
|
||||
type FileGroupDescriptor struct {
|
||||
CItems uint32 `struc:"little"`
|
||||
Fgd []FileDescriptor `struc:"sizefrom=CItems"`
|
||||
}
|
||||
type FileDescriptor struct {
|
||||
Flags uint32 `struc:"little"`
|
||||
Clsid [16]byte `struc:"little"`
|
||||
Sizel [8]byte `struc:"little"`
|
||||
Pointl [8]byte `struc:"little"`
|
||||
FileAttributes uint32 `struc:"little"`
|
||||
CreationTime [8]byte `struc:"little"`
|
||||
LastAccessTime [8]byte `struc:"little"`
|
||||
LastWriteTime []byte `struc:"[8]byte"` //8
|
||||
FileSizeHigh uint32 `struc:"little"`
|
||||
FileSizeLow uint32 `struc:"little"`
|
||||
FileName []byte `struc:"[512]byte"`
|
||||
}
|
||||
|
||||
func (f *FileGroupDescriptor) Unpack(b []byte) error {
|
||||
r := bytes.NewReader(b)
|
||||
err := struc.Unpack(r, f)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (f *FileDescriptor) serialize() []byte {
|
||||
b := &bytes.Buffer{}
|
||||
core.WriteUInt32LE(f.Flags, b)
|
||||
for i := 0; i < 32; i++ {
|
||||
core.WriteByte(0, b)
|
||||
}
|
||||
core.WriteUInt32LE(f.FileAttributes, b)
|
||||
for i := 0; i < 16; i++ {
|
||||
core.WriteByte(0, b)
|
||||
}
|
||||
core.WriteBytes(f.LastWriteTime[:], b)
|
||||
core.WriteUInt32LE(f.FileSizeHigh, b)
|
||||
core.WriteUInt32LE(f.FileSizeLow, b)
|
||||
name := make([]byte, 512)
|
||||
copy(name, f.FileName)
|
||||
core.WriteBytes(name, b)
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
func (f *FileDescriptor) isDir() bool {
|
||||
if f.Flags&FD_ATTRIBUTES != 0 {
|
||||
return f.FileAttributes&FILE_ATTRIBUTE_DIRECTORY != 0
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *FileDescriptor) hasFileSize() bool {
|
||||
return f.Flags&FD_FILESIZE != 0
|
||||
}
|
||||
|
||||
// temp dir
|
||||
type CliprdrTempDirectory struct {
|
||||
SzTempDir []byte `struc:"[260]byte"`
|
||||
}
|
||||
|
||||
// format list
|
||||
type CliprdrFormat struct {
|
||||
FormatId uint32
|
||||
FormatName string
|
||||
}
|
||||
type CliprdrFormatList struct {
|
||||
NumFormats uint32
|
||||
Formats []CliprdrFormat
|
||||
}
|
||||
type ClipboardFormats uint16
|
||||
|
||||
const (
|
||||
CB_FORMAT_HTML = 0xD010
|
||||
CB_FORMAT_PNG = 0xD011
|
||||
CB_FORMAT_JPEG = 0xD012
|
||||
CB_FORMAT_GIF = 0xD013
|
||||
CB_FORMAT_TEXTURILIST = 0xD014
|
||||
CB_FORMAT_GNOMECOPIEDFILES = 0xD015
|
||||
CB_FORMAT_MATECOPIEDFILES = 0xD016
|
||||
)
|
||||
|
||||
// lock or unlock
|
||||
type CliprdrCtrlClipboardData struct {
|
||||
ClipDataId uint32
|
||||
}
|
||||
|
||||
// format data
|
||||
type CliprdrFormatDataRequest struct {
|
||||
RequestedFormatId uint32
|
||||
}
|
||||
type CliprdrFormatDataResponse struct {
|
||||
RequestedFormatData []byte
|
||||
}
|
||||
|
||||
// file contents
|
||||
type CliprdrFileContentsRequest struct {
|
||||
StreamId uint32 `struc:"little"`
|
||||
Lindex uint32 `struc:"little"`
|
||||
DwFlags uint32 `struc:"little"`
|
||||
NPositionLow uint32 `struc:"little"`
|
||||
NPositionHigh uint32 `struc:"little"`
|
||||
CbRequested uint32 `struc:"little"`
|
||||
ClipDataId uint32 `struc:"little"`
|
||||
}
|
||||
|
||||
func FileContentsSizeRequest(i uint32) *CliprdrFileContentsRequest {
|
||||
return &CliprdrFileContentsRequest{
|
||||
StreamId: 1,
|
||||
Lindex: i,
|
||||
DwFlags: FILECONTENTS_SIZE,
|
||||
NPositionLow: 0,
|
||||
NPositionHigh: 0,
|
||||
CbRequested: 65535,
|
||||
ClipDataId: 0,
|
||||
}
|
||||
}
|
||||
|
||||
type CliprdrFileContentsResponse struct {
|
||||
StreamId uint32
|
||||
CbRequested uint32
|
||||
RequestedData []byte
|
||||
}
|
||||
|
||||
func (resp *CliprdrFileContentsResponse) Unpack(b []byte) {
|
||||
r := bytes.NewReader(b)
|
||||
resp.StreamId, _ = core.ReadUInt32LE(r)
|
||||
resp.CbRequested = uint32(r.Len())
|
||||
resp.RequestedData, _ = core.ReadBytes(int(resp.CbRequested), r)
|
||||
}
|
||||
|
||||
type CliprdrClient struct {
|
||||
w core.ChannelSender
|
||||
useLongFormatNames bool
|
||||
streamFileClipEnabled bool
|
||||
fileClipNoFilePaths bool
|
||||
canLockClipData bool
|
||||
hasHugeFileSupport bool
|
||||
formatIdMap map[uint32]uint32
|
||||
Files []FileDescriptor
|
||||
reply chan []byte
|
||||
Control
|
||||
}
|
||||
|
||||
func NewCliprdrClient() *CliprdrClient {
|
||||
c := &CliprdrClient{
|
||||
formatIdMap: make(map[uint32]uint32, 20),
|
||||
Files: make([]FileDescriptor, 0, 20),
|
||||
reply: make(chan []byte, 100),
|
||||
}
|
||||
|
||||
go ClipWatcher(c)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) Send(s []byte) (int, error) {
|
||||
glog.Debug("len:", len(s), "data:", hex.EncodeToString(s))
|
||||
name, _ := c.GetType()
|
||||
return c.w.SendToChannel(name, s)
|
||||
}
|
||||
func (c *CliprdrClient) Sender(f core.ChannelSender) {
|
||||
c.w = f
|
||||
}
|
||||
func (c *CliprdrClient) GetType() (string, uint32) {
|
||||
return ChannelName, ChannelOption
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) Process(s []byte) {
|
||||
glog.Debug("recv:", hex.EncodeToString(s))
|
||||
r := bytes.NewReader(s)
|
||||
|
||||
msgType, _ := core.ReadUint16LE(r)
|
||||
flag, _ := core.ReadUint16LE(r)
|
||||
length, _ := core.ReadUInt32LE(r)
|
||||
glog.Debugf("cliprdr: type=0x%x flag=%d length=%d, all=%d", msgType, flag, length, r.Len())
|
||||
|
||||
b, _ := core.ReadBytes(int(length), r)
|
||||
|
||||
switch msgType {
|
||||
case CB_CLIP_CAPS:
|
||||
glog.Info("CB_CLIP_CAPS")
|
||||
c.processClipCaps(b)
|
||||
|
||||
case CB_MONITOR_READY:
|
||||
glog.Info("CB_MONITOR_READY")
|
||||
c.processMonitorReady(b)
|
||||
|
||||
case CB_FORMAT_LIST:
|
||||
glog.Info("CB_FORMAT_LIST")
|
||||
c.processFormatList(b)
|
||||
|
||||
case CB_FORMAT_LIST_RESPONSE:
|
||||
glog.Info("CB_FORMAT_LIST_RESPONSE")
|
||||
c.processFormatListResponse(flag, b)
|
||||
|
||||
case CB_FORMAT_DATA_REQUEST:
|
||||
glog.Info("CB_FORMAT_DATA_REQUEST")
|
||||
c.processFormatDataRequest(b)
|
||||
|
||||
case CB_FORMAT_DATA_RESPONSE:
|
||||
glog.Info("CB_FORMAT_DATA_RESPONSE")
|
||||
c.processFormatDataResponse(flag, b)
|
||||
|
||||
case CB_FILECONTENTS_REQUEST:
|
||||
glog.Info("CB_FILECONTENTS_REQUEST")
|
||||
c.processFileContentsRequest(b)
|
||||
|
||||
case CB_FILECONTENTS_RESPONSE:
|
||||
glog.Info("CB_FILECONTENTS_RESPONSE")
|
||||
c.processFileContentsResponse(flag, b)
|
||||
|
||||
case CB_LOCK_CLIPDATA:
|
||||
glog.Info("CB_LOCK_CLIPDATA")
|
||||
c.processLockClipData(b)
|
||||
|
||||
case CB_UNLOCK_CLIPDATA:
|
||||
glog.Info("CB_UNLOCK_CLIPDATA")
|
||||
c.processUnlockClipData(b)
|
||||
|
||||
default:
|
||||
glog.Errorf("type 0x%x not supported", msgType)
|
||||
}
|
||||
}
|
||||
func (c *CliprdrClient) processClipCaps(b []byte) {
|
||||
r := bytes.NewReader(b)
|
||||
var cp CliprdrCapabilitiesPDU
|
||||
err := struc.Unpack(r, &cp)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
glog.Debugf("Capabilities:%+v", cp)
|
||||
c.useLongFormatNames = cp.CapabilitySets[0].GeneralFlags&CB_USE_LONG_FORMAT_NAMES != 0
|
||||
c.streamFileClipEnabled = cp.CapabilitySets[0].GeneralFlags&CB_STREAM_FILECLIP_ENABLED != 0
|
||||
c.fileClipNoFilePaths = cp.CapabilitySets[0].GeneralFlags&CB_FILECLIP_NO_FILE_PATHS != 0
|
||||
c.canLockClipData = cp.CapabilitySets[0].GeneralFlags&CB_CAN_LOCK_CLIPDATA != 0
|
||||
c.hasHugeFileSupport = cp.CapabilitySets[0].GeneralFlags&CB_HUGE_FILE_SUPPORT_ENABLED != 0
|
||||
glog.Info("UseLongFormatNames:", c.useLongFormatNames)
|
||||
glog.Info("StreamFileClipEnabled:", c.streamFileClipEnabled)
|
||||
glog.Info("FileClipNoFilePaths:", c.fileClipNoFilePaths)
|
||||
glog.Info("CanLockClipData:", c.canLockClipData)
|
||||
glog.Info("HasHugeFileSupport:", c.hasHugeFileSupport)
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) processMonitorReady(b []byte) {
|
||||
//Client Clipboard Capabilities PDU
|
||||
c.sendClientCapabilitiesPDU()
|
||||
|
||||
//Temporary Directory PDU
|
||||
//c.sendTemporaryDirectoryPDU()
|
||||
|
||||
//Format List PDU
|
||||
c.sendFormatListPDU()
|
||||
|
||||
}
|
||||
func (c *CliprdrClient) processFormatList(b []byte) {
|
||||
c.withOpenClipboard(func() {
|
||||
if !EmptyClipboard() {
|
||||
glog.Error("EmptyClipboard failed")
|
||||
}
|
||||
})
|
||||
fl, hasFile := c.readForamtList(b)
|
||||
glog.Info("numFormats:", fl.NumFormats)
|
||||
|
||||
if hasFile {
|
||||
c.SendCliprdrMessage()
|
||||
} else {
|
||||
c.withOpenClipboard(func() {
|
||||
if !EmptyClipboard() {
|
||||
glog.Error("EmptyClipboard failed")
|
||||
}
|
||||
for i := range c.formatIdMap {
|
||||
glog.Debug("i:", i)
|
||||
SetClipboardData(i, 0)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
c.sendFormatListResponse(CB_RESPONSE_OK)
|
||||
}
|
||||
func (c *CliprdrClient) processFormatListResponse(flag uint16, b []byte) {
|
||||
if flag != CB_RESPONSE_OK {
|
||||
glog.Error("Format List Response Failed")
|
||||
return
|
||||
}
|
||||
glog.Error("Format List Response OK")
|
||||
}
|
||||
func getFilesDescriptor(name string) (FileDescriptor, error) {
|
||||
var fd FileDescriptor
|
||||
fd.Flags = FD_ATTRIBUTES | FD_FILESIZE | FD_WRITESTIME | FD_PROGRESSUI
|
||||
f, e := os.Stat(name)
|
||||
if e != nil {
|
||||
glog.Error(e.Error())
|
||||
return fd, e
|
||||
}
|
||||
fd.FileAttributes, fd.LastWriteTime,
|
||||
fd.FileSizeHigh, fd.FileSizeLow = GetFileInfo(f.Sys())
|
||||
fd.FileName = core.UnicodeEncode(name)
|
||||
|
||||
return fd, nil
|
||||
}
|
||||
func (c *CliprdrClient) processFormatDataRequest(b []byte) {
|
||||
r := bytes.NewReader(b)
|
||||
requestId, _ := core.ReadUInt32LE(r)
|
||||
|
||||
buff := &bytes.Buffer{}
|
||||
if requestId == RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW) {
|
||||
fs := GetFileNames()
|
||||
core.WriteUInt32LE(uint32(len(fs)), buff)
|
||||
c.Files = c.Files[:0]
|
||||
for _, v := range fs {
|
||||
glog.Info("Name:", v)
|
||||
f, _ := getFilesDescriptor(v)
|
||||
buff.Write(f.serialize())
|
||||
for i := 0; i < 8; i++ {
|
||||
buff.WriteByte(0)
|
||||
}
|
||||
c.Files = append(c.Files, f)
|
||||
|
||||
}
|
||||
} else {
|
||||
c.withOpenClipboard(func() {
|
||||
data := GetClipboardData(requestId)
|
||||
glog.Debug("data:", data)
|
||||
buff.Write(core.UnicodeEncode(data))
|
||||
buff.Write([]byte{0, 0})
|
||||
})
|
||||
}
|
||||
|
||||
c.sendFormatDataResponse(buff.Bytes())
|
||||
}
|
||||
func (c *CliprdrClient) processFormatDataResponse(flag uint16, b []byte) {
|
||||
if flag != CB_RESPONSE_OK {
|
||||
glog.Error("Format Data Response Failed")
|
||||
}
|
||||
c.reply <- b
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) processFileContentsRequest(b []byte) {
|
||||
r := bytes.NewReader(b)
|
||||
var req CliprdrFileContentsRequest
|
||||
struc.Unpack(r, &req)
|
||||
if len(c.Files) <= int(req.Lindex) {
|
||||
glog.Error("No found file:", req.Lindex)
|
||||
c.sendFormatContentsResponse(req.StreamId, []byte{})
|
||||
return
|
||||
}
|
||||
buff := &bytes.Buffer{}
|
||||
/*o := OleGetClipboard()
|
||||
var format_etc FORMATETC
|
||||
var stg_medium STGMEDIUM
|
||||
format_etc.CFormat = RegisterClipboardFormat(CFSTR_FILECONTENTS)
|
||||
format_etc.Tymed = TYMED_ISTREAM
|
||||
format_etc.Aspect = 1
|
||||
format_etc.Index = req.Lindex
|
||||
o.GetData(&format_etc, &stg_medium)
|
||||
s, _ := stg_medium.Stream()*/
|
||||
f := c.Files[req.Lindex]
|
||||
if req.DwFlags == FILECONTENTS_SIZE {
|
||||
core.WriteUInt32LE(f.FileSizeLow, buff)
|
||||
core.WriteUInt32LE(f.FileSizeHigh, buff)
|
||||
c.sendFormatContentsResponse(req.StreamId, buff.Bytes())
|
||||
} else if req.DwFlags == FILECONTENTS_RANGE {
|
||||
name := core.UnicodeDecode(f.FileName)
|
||||
fi, err := os.Open(name)
|
||||
if err != nil {
|
||||
glog.Error(err.Error())
|
||||
return
|
||||
}
|
||||
defer fi.Close()
|
||||
data := make([]byte, req.CbRequested)
|
||||
n, _ := fi.ReadAt(data, int64(f.FileSizeHigh))
|
||||
c.sendFormatContentsResponse(req.StreamId, data[:n])
|
||||
}
|
||||
}
|
||||
func (c *CliprdrClient) processFileContentsResponse(flag uint16, b []byte) {
|
||||
if flag != CB_RESPONSE_OK {
|
||||
glog.Error("File Contents Response Failed")
|
||||
}
|
||||
var resp CliprdrFileContentsResponse
|
||||
resp.Unpack(b)
|
||||
glog.Debug("Get File Contents Response:", resp.StreamId, resp.CbRequested)
|
||||
c.reply <- resp.RequestedData
|
||||
}
|
||||
func (c *CliprdrClient) processLockClipData(b []byte) {
|
||||
r := bytes.NewReader(b)
|
||||
var l CliprdrCtrlClipboardData
|
||||
l.ClipDataId, _ = core.ReadUInt32LE(r)
|
||||
}
|
||||
func (c *CliprdrClient) processUnlockClipData(b []byte) {
|
||||
r := bytes.NewReader(b)
|
||||
var l CliprdrCtrlClipboardData
|
||||
l.ClipDataId, _ = core.ReadUInt32LE(r)
|
||||
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) sendClientCapabilitiesPDU() {
|
||||
glog.Info("Send Client Clipboard Capabilities PDU")
|
||||
var cs CliprdrGeneralCapabilitySet
|
||||
cs.CapabilitySetLength = 12
|
||||
cs.CapabilitySetType = CB_CAPSTYPE_GENERAL
|
||||
cs.Version = CB_CAPS_VERSION_2
|
||||
cs.GeneralFlags = CB_USE_LONG_FORMAT_NAMES |
|
||||
CB_STREAM_FILECLIP_ENABLED |
|
||||
CB_FILECLIP_NO_FILE_PATHS
|
||||
var cc CliprdrCapabilitiesPDU
|
||||
cc.CCapabilitiesSets = 1
|
||||
cc.Pad1 = 0
|
||||
cc.CapabilitySets = make([]CliprdrGeneralCapabilitySet, 0, 1)
|
||||
cc.CapabilitySets = append(cc.CapabilitySets, cs)
|
||||
header := NewCliprdrPDUHeader(CB_CLIP_CAPS, 0, 16)
|
||||
|
||||
buff := &bytes.Buffer{}
|
||||
buff.Write(header.serialize())
|
||||
core.WriteUInt16LE(cc.CCapabilitiesSets, buff)
|
||||
core.WriteUInt16LE(cc.Pad1, buff)
|
||||
for _, v := range cc.CapabilitySets {
|
||||
struc.Pack(buff, v)
|
||||
}
|
||||
|
||||
c.Send(buff.Bytes())
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) sendTemporaryDirectoryPDU() {
|
||||
glog.Info("Send Temporary Directory PDU")
|
||||
var t CliprdrTempDirectory
|
||||
header := &CliprdrPDUHeader{CB_TEMP_DIRECTORY, 0, 260}
|
||||
t.SzTempDir = core.UnicodeEncode(os.TempDir())
|
||||
|
||||
buff := &bytes.Buffer{}
|
||||
core.WriteBytes(header.serialize(), buff)
|
||||
core.WriteBytes(t.SzTempDir, buff)
|
||||
c.Send(buff.Bytes())
|
||||
}
|
||||
func (c *CliprdrClient) sendFormatListPDU() {
|
||||
glog.Info("Send Format List PDU")
|
||||
var f CliprdrFormatList
|
||||
|
||||
f.Formats = GetFormatList(c.hwnd)
|
||||
f.NumFormats = uint32(len(f.Formats))
|
||||
|
||||
glog.Info("NumFormats:", f.NumFormats)
|
||||
glog.Debug("Formats:", f.Formats)
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
for _, v := range f.Formats {
|
||||
core.WriteUInt32LE(v.FormatId, b)
|
||||
if v.FormatName == "" {
|
||||
core.WriteUInt16LE(0, b)
|
||||
} else {
|
||||
n := core.UnicodeEncode(v.FormatName)
|
||||
core.WriteBytes(n, b)
|
||||
b.Write([]byte{0, 0})
|
||||
}
|
||||
}
|
||||
|
||||
header := NewCliprdrPDUHeader(CB_FORMAT_LIST, 0, uint32(b.Len()))
|
||||
|
||||
buff := &bytes.Buffer{}
|
||||
buff.Write(header.serialize())
|
||||
core.WriteBytes(b.Bytes(), buff)
|
||||
|
||||
c.Send(buff.Bytes())
|
||||
}
|
||||
func (c *CliprdrClient) readForamtList(b []byte) (*CliprdrFormatList, bool) {
|
||||
r := bytes.NewReader(b)
|
||||
fs := make([]CliprdrFormat, 0, 20)
|
||||
var numFormats uint32 = 0
|
||||
hasFile := false
|
||||
c.formatIdMap = make(map[uint32]uint32, 0)
|
||||
for r.Len() > 0 {
|
||||
foramtId, _ := core.ReadUInt32LE(r)
|
||||
bs := make([]uint16, 0, 20)
|
||||
ln := r.Len()
|
||||
for j := 0; j < ln; j++ {
|
||||
b, _ := core.ReadUint16LE(r)
|
||||
if b == 0 {
|
||||
break
|
||||
}
|
||||
bs = append(bs, b)
|
||||
}
|
||||
name := string(utf16.Decode(bs))
|
||||
if strings.EqualFold(name, CFSTR_FILEDESCRIPTORW) {
|
||||
hasFile = true
|
||||
}
|
||||
glog.Infof("Foramt:%d Name:<%s>", foramtId, name)
|
||||
if name != "" {
|
||||
localId := RegisterClipboardFormat(name)
|
||||
glog.Info("local:", localId, "remote:", foramtId)
|
||||
c.formatIdMap[localId] = foramtId
|
||||
} else {
|
||||
c.formatIdMap[foramtId] = foramtId
|
||||
}
|
||||
|
||||
numFormats++
|
||||
fs = append(fs, CliprdrFormat{foramtId, name})
|
||||
}
|
||||
|
||||
return &CliprdrFormatList{numFormats, fs}, hasFile
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) sendFormatListResponse(flags uint16) {
|
||||
glog.Info("Send Format List Response")
|
||||
header := NewCliprdrPDUHeader(CB_FORMAT_LIST_RESPONSE, flags, 0)
|
||||
buff := &bytes.Buffer{}
|
||||
buff.Write(header.serialize())
|
||||
c.Send(buff.Bytes())
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) sendFormatDataRequest(id uint32) {
|
||||
glog.Info("Send Format Data Request")
|
||||
var r CliprdrFormatDataRequest
|
||||
r.RequestedFormatId = id
|
||||
header := NewCliprdrPDUHeader(CB_FORMAT_DATA_REQUEST, 0, 4)
|
||||
|
||||
buff := &bytes.Buffer{}
|
||||
buff.Write(header.serialize())
|
||||
core.WriteUInt32LE(r.RequestedFormatId, buff)
|
||||
|
||||
c.Send(buff.Bytes())
|
||||
}
|
||||
func (c *CliprdrClient) sendFormatDataResponse(b []byte) {
|
||||
glog.Info("Send Format Data Response")
|
||||
var resp CliprdrFormatDataResponse
|
||||
resp.RequestedFormatData = b
|
||||
|
||||
header := NewCliprdrPDUHeader(CB_FORMAT_DATA_RESPONSE, CB_RESPONSE_OK, uint32(len(resp.RequestedFormatData)))
|
||||
|
||||
buff := &bytes.Buffer{}
|
||||
buff.Write(header.serialize())
|
||||
buff.Write(resp.RequestedFormatData)
|
||||
|
||||
c.Send(buff.Bytes())
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) sendFormatContentsRequest(r CliprdrFileContentsRequest) uint32 {
|
||||
glog.Info("Send Format Contents Request")
|
||||
glog.Debugf("Format Contents Request:%+v", r)
|
||||
header := NewCliprdrPDUHeader(CB_FILECONTENTS_REQUEST, 0, 28)
|
||||
|
||||
buff := &bytes.Buffer{}
|
||||
buff.Write(header.serialize())
|
||||
core.WriteUInt32LE(r.StreamId, buff)
|
||||
core.WriteUInt32LE(uint32(r.Lindex), buff)
|
||||
core.WriteUInt32LE(r.DwFlags, buff)
|
||||
core.WriteUInt32LE(r.NPositionLow, buff)
|
||||
core.WriteUInt32LE(r.NPositionHigh, buff)
|
||||
core.WriteUInt32LE(r.CbRequested, buff)
|
||||
core.WriteUInt32LE(r.ClipDataId, buff)
|
||||
|
||||
c.Send(buff.Bytes())
|
||||
|
||||
return uint32(buff.Len())
|
||||
}
|
||||
func (c *CliprdrClient) sendFormatContentsResponse(streamId uint32, b []byte) {
|
||||
glog.Info("Send Format Contents Response")
|
||||
var r CliprdrFileContentsResponse
|
||||
r.StreamId = streamId
|
||||
r.RequestedData = b
|
||||
r.CbRequested = uint32(len(b))
|
||||
header := NewCliprdrPDUHeader(CB_FILECONTENTS_RESPONSE, CB_RESPONSE_OK, uint32(4+r.CbRequested))
|
||||
|
||||
buff := &bytes.Buffer{}
|
||||
buff.Write(header.serialize())
|
||||
core.WriteUInt32LE(r.StreamId, buff)
|
||||
core.WriteBytes(r.RequestedData, buff)
|
||||
|
||||
c.Send(buff.Bytes())
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) sendLockClipData() {
|
||||
glog.Info("Send Lock Clip Data")
|
||||
var r CliprdrCtrlClipboardData
|
||||
header := NewCliprdrPDUHeader(CB_LOCK_CLIPDATA, 0, 4)
|
||||
|
||||
buff := &bytes.Buffer{}
|
||||
buff.Write(header.serialize())
|
||||
core.WriteUInt32LE(r.ClipDataId, buff)
|
||||
|
||||
c.Send(buff.Bytes())
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) sendUnlockClipData() {
|
||||
glog.Info("Send Unlock Clip Data")
|
||||
var r CliprdrCtrlClipboardData
|
||||
header := NewCliprdrPDUHeader(CB_UNLOCK_CLIPDATA, 0, 4)
|
||||
|
||||
buff := &bytes.Buffer{}
|
||||
buff.Write(header.serialize())
|
||||
core.WriteUInt32LE(r.ClipDataId, buff)
|
||||
|
||||
c.Send(buff.Bytes())
|
||||
}
|
||||
22
grdp/plugin/cliprdr/cliprdr_test.go
Normal file
22
grdp/plugin/cliprdr/cliprdr_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// cliprdr_test.go
|
||||
package cliprdr_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"ShotRDP/grdp/plugin/cliprdr"
|
||||
)
|
||||
|
||||
func TestClip(t *testing.T) {
|
||||
//t1, _ := cliprdr.ReadAll()
|
||||
//fmt.Printf("%s\n", t1)
|
||||
ok := cliprdr.OpenClipboard(0)
|
||||
fmt.Println(ok)
|
||||
if ok {
|
||||
name := cliprdr.GetClipboardData(13)
|
||||
fmt.Printf("name=%s\n", name)
|
||||
|
||||
cliprdr.CloseClipboard()
|
||||
}
|
||||
}
|
||||
354
grdp/plugin/cliprdr/cliprdr_windows.go
Normal file
354
grdp/plugin/cliprdr/cliprdr_windows.go
Normal file
@@ -0,0 +1,354 @@
|
||||
// cliprdr_windows.go
|
||||
package cliprdr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
|
||||
"ShotRDP/grdp/glog"
|
||||
"github.com/shirou/w32"
|
||||
|
||||
"ShotRDP/grdp/core"
|
||||
|
||||
"ShotRDP/grdp/win"
|
||||
)
|
||||
|
||||
const (
|
||||
CFSTR_SHELLIDLIST = "Shell IDList Array"
|
||||
CFSTR_SHELLIDLISTOFFSET = "Shell Object Offsets"
|
||||
CFSTR_NETRESOURCES = "Net Resource"
|
||||
CFSTR_FILECONTENTS = "FileContents"
|
||||
CFSTR_FILENAMEA = "FileName"
|
||||
CFSTR_FILENAMEMAPA = "FileNameMap"
|
||||
CFSTR_FILEDESCRIPTORA = "FileGroupDescriptor"
|
||||
CFSTR_INETURLA = "UniformResourceLocator"
|
||||
CFSTR_SHELLURL = CFSTR_INETURLA
|
||||
CFSTR_FILENAMEW = "FileNameW"
|
||||
CFSTR_FILENAMEMAPW = "FileNameMapW"
|
||||
CFSTR_FILEDESCRIPTORW = "FileGroupDescriptorW"
|
||||
CFSTR_INETURLW = "UniformResourceLocatorW"
|
||||
CFSTR_PRINTERGROUP = "PrinterFriendlyName"
|
||||
CFSTR_INDRAGLOOP = "InShellDragLoop"
|
||||
CFSTR_PASTESUCCEEDED = "Paste Succeeded"
|
||||
CFSTR_PERFORMEDDROPEFFECT = "Performed DropEffect"
|
||||
CFSTR_PREFERREDDROPEFFECT = "Preferred DropEffect"
|
||||
)
|
||||
const DVASPECT_CONTENT = 0x1
|
||||
|
||||
const (
|
||||
CF_TEXT = 1
|
||||
CF_BITMAP = 2
|
||||
CF_METAFILEPICT = 3
|
||||
CF_SYLK = 4
|
||||
CF_DIF = 5
|
||||
CF_TIFF = 6
|
||||
CF_OEMTEXT = 7
|
||||
CF_DIB = 8
|
||||
CF_PALETTE = 9
|
||||
CF_PENDATA = 10
|
||||
CF_RIFF = 11
|
||||
CF_WAVE = 12
|
||||
CF_UNICODETEXT = 13
|
||||
CF_ENHMETAFILE = 14
|
||||
CF_HDROP = 15
|
||||
CF_LOCALE = 16
|
||||
CF_DIBV5 = 17
|
||||
CF_MAX = 18
|
||||
)
|
||||
const (
|
||||
WM_CLIPRDR_MESSAGE = (w32.WM_USER + 156)
|
||||
OLE_SETCLIPBOARD = 1
|
||||
)
|
||||
|
||||
type Control struct {
|
||||
hwnd uintptr
|
||||
dataObject *IDataObject
|
||||
}
|
||||
|
||||
func (c *Control) withOpenClipboard(f func()) {
|
||||
if OpenClipboard(c.hwnd) {
|
||||
f()
|
||||
CloseClipboard()
|
||||
}
|
||||
}
|
||||
func ClipWatcher(c *CliprdrClient) {
|
||||
win.OleInitialize(0)
|
||||
defer win.OleUninitialize()
|
||||
className := syscall.StringToUTF16Ptr("ClipboardHiddenMessageProcessor")
|
||||
windowName := syscall.StringToUTF16Ptr("rdpclip")
|
||||
wndClassEx := w32.WNDCLASSEX{
|
||||
ClassName: className,
|
||||
WndProc: syscall.NewCallback(func(hwnd w32.HWND, msg uint32, wParam, lParam uintptr) uintptr {
|
||||
switch msg {
|
||||
case w32.WM_CLIPBOARDUPDATE:
|
||||
glog.Info("info: WM_CLIPBOARDUPDATE wParam:", wParam)
|
||||
glog.Debug("IsClipboardOwner:", IsClipboardOwner(win.HWND(c.hwnd)))
|
||||
glog.Debug("OleIsCurrentClipboard:", OleIsCurrentClipboard(c.dataObject))
|
||||
if !IsClipboardOwner(win.HWND(c.hwnd)) && int(wParam) != 0 &&
|
||||
!OleIsCurrentClipboard(c.dataObject) {
|
||||
c.sendFormatListPDU()
|
||||
}
|
||||
|
||||
case w32.WM_RENDERALLFORMATS:
|
||||
glog.Info("info: WM_RENDERALLFORMATS")
|
||||
c.withOpenClipboard(func() {
|
||||
EmptyClipboard()
|
||||
})
|
||||
|
||||
case w32.WM_RENDERFORMAT:
|
||||
glog.Info("info: WM_RENDERFORMAT wParam:", wParam)
|
||||
formatId := uint32(wParam)
|
||||
c.sendFormatDataRequest(formatId)
|
||||
b := <-c.reply
|
||||
hmem := HmemAlloc(b)
|
||||
SetClipboardData(formatId, hmem)
|
||||
|
||||
case WM_CLIPRDR_MESSAGE:
|
||||
glog.Info("info: WM_CLIPRDR_MESSAGE wParam:", wParam)
|
||||
if wParam == OLE_SETCLIPBOARD {
|
||||
if !OleIsCurrentClipboard(c.dataObject) {
|
||||
o := CreateDataObject(c)
|
||||
OleSetClipboard(o)
|
||||
c.dataObject = o
|
||||
}
|
||||
}
|
||||
default:
|
||||
return w32.DefWindowProc(hwnd, msg, wParam, lParam)
|
||||
}
|
||||
return 0
|
||||
}),
|
||||
Style: w32.CS_OWNDC,
|
||||
}
|
||||
wndClassEx.Size = uint32(unsafe.Sizeof(wndClassEx))
|
||||
w32.RegisterClassEx(&wndClassEx)
|
||||
|
||||
hwnd := w32.CreateWindowEx(w32.WS_EX_LEFT, className, windowName, 0, 0, 0, 0, 0, w32.HWND_MESSAGE, 0, 0, nil)
|
||||
c.hwnd = uintptr(hwnd)
|
||||
w32.AddClipboardFormatListener(hwnd)
|
||||
defer w32.RemoveClipboardFormatListener(hwnd)
|
||||
|
||||
msg := w32.MSG{}
|
||||
for w32.GetMessage(&msg, 0, 0, 0) > 0 {
|
||||
w32.DispatchMessage(&msg)
|
||||
}
|
||||
|
||||
}
|
||||
func OpenClipboard(hwnd uintptr) bool {
|
||||
return win.OpenClipboard(win.HWND(hwnd))
|
||||
}
|
||||
func CloseClipboard() bool {
|
||||
return win.CloseClipboard()
|
||||
}
|
||||
func CountClipboardFormats() int32 {
|
||||
return win.CountClipboardFormats()
|
||||
}
|
||||
func IsClipboardFormatAvailable(id uint32) bool {
|
||||
return win.IsClipboardFormatAvailable(win.UINT(id))
|
||||
}
|
||||
func EnumClipboardFormats(formatId uint32) uint32 {
|
||||
id := win.EnumClipboardFormats(win.UINT(formatId))
|
||||
return uint32(id)
|
||||
}
|
||||
func GetClipboardFormatName(id uint32) string {
|
||||
buf := make([]uint16, 250)
|
||||
n := win.GetClipboardFormatName(win.UINT(id), win.LPWSTR(unsafe.Pointer(&buf[0])), int32(len(buf)))
|
||||
return string(utf16.Decode(buf[:n]))
|
||||
}
|
||||
func EmptyClipboard() bool {
|
||||
return win.EmptyClipboard()
|
||||
}
|
||||
func RegisterClipboardFormat(format string) uint32 {
|
||||
id := win.RegisterClipboardFormat(format)
|
||||
return uint32(id)
|
||||
}
|
||||
func IsClipboardOwner(h win.HWND) bool {
|
||||
hwnd := win.GetClipboardOwner()
|
||||
return h == hwnd
|
||||
}
|
||||
|
||||
func HmemAlloc(data []byte) uintptr {
|
||||
ln := (len(data))
|
||||
h := win.GlobalAlloc(0x0002, win.SIZE_T(ln))
|
||||
if h == 0 {
|
||||
return uintptr(h)
|
||||
}
|
||||
if ln == 0 {
|
||||
return uintptr(h)
|
||||
}
|
||||
l := win.GlobalLock(h)
|
||||
defer win.GlobalUnlock(h)
|
||||
|
||||
win.RtlCopyMemory(uintptr(unsafe.Pointer(l)), uintptr(unsafe.Pointer(&data[0])), win.SIZE_T(ln))
|
||||
|
||||
return uintptr(h)
|
||||
|
||||
}
|
||||
func SetClipboardData(formatId uint32, hmem uintptr) bool {
|
||||
r := win.SetClipboardData(win.UINT(formatId), win.HANDLE(hmem))
|
||||
if r == 0 {
|
||||
//glog.Error("SetClipboardData failed:", formatId, hmem)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
func GetClipboardData(formatId uint32) string {
|
||||
r := win.GetClipboardData(win.UINT(formatId))
|
||||
if r == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
h := win.GlobalHandle(uintptr(r))
|
||||
size := win.GlobalSize(h)
|
||||
l := win.GlobalLock(h)
|
||||
defer win.GlobalUnlock(h)
|
||||
|
||||
result := make([]byte, size)
|
||||
win.RtlCopyMemory(uintptr(unsafe.Pointer(&result[0])), uintptr(unsafe.Pointer(l)), size)
|
||||
|
||||
return core.UnicodeDecode(result)
|
||||
}
|
||||
|
||||
func GetFormatList(hwnd uintptr) []CliprdrFormat {
|
||||
list := make([]CliprdrFormat, 0, 10)
|
||||
if OpenClipboard(hwnd) {
|
||||
n := CountClipboardFormats()
|
||||
if IsClipboardFormatAvailable(CF_HDROP) {
|
||||
formatId := RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW)
|
||||
var f CliprdrFormat
|
||||
f.FormatId = formatId
|
||||
f.FormatName = CFSTR_FILEDESCRIPTORW
|
||||
list = append(list, f)
|
||||
formatId = RegisterClipboardFormat(CFSTR_FILECONTENTS)
|
||||
var f1 CliprdrFormat
|
||||
f1.FormatId = formatId
|
||||
f1.FormatName = CFSTR_FILECONTENTS
|
||||
list = append(list, f1)
|
||||
} else {
|
||||
var id uint32
|
||||
for i := 0; i < int(n); i++ {
|
||||
id = EnumClipboardFormats(id)
|
||||
name := GetClipboardFormatName(id)
|
||||
var f CliprdrFormat
|
||||
f.FormatId = id
|
||||
f.FormatName = name
|
||||
list = append(list, f)
|
||||
}
|
||||
}
|
||||
CloseClipboard()
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func OleGetClipboard() *IDataObject {
|
||||
var dataObject *IDataObject
|
||||
win.OleGetClipboard((**win.IDataObject)(unsafe.Pointer(&dataObject)))
|
||||
return dataObject
|
||||
}
|
||||
|
||||
func OleSetClipboard(dataObject *IDataObject) bool {
|
||||
r := win.OleSetClipboard((*win.IDataObject)(unsafe.Pointer(dataObject)))
|
||||
if r != 0 {
|
||||
glog.Error("OleSetClipboard failed")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func OleIsCurrentClipboard(dataObject *IDataObject) bool {
|
||||
r := win.OleIsCurrentClipboard((*win.IDataObject)(unsafe.Pointer(dataObject)))
|
||||
if r != 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
func GlobalSize(hMem uintptr) win.SIZE_T {
|
||||
return win.GlobalSize(win.HGLOBAL(hMem))
|
||||
}
|
||||
func GlobalLock(hMem uintptr) uintptr {
|
||||
r := win.GlobalLock(win.HGLOBAL(hMem))
|
||||
|
||||
return uintptr(r)
|
||||
}
|
||||
func GlobalUnlock(hMem uintptr) {
|
||||
win.GlobalUnlock(win.HGLOBAL(hMem))
|
||||
}
|
||||
|
||||
func (c *Control) SendCliprdrMessage() {
|
||||
win.PostMessage(win.HWND(c.hwnd), WM_CLIPRDR_MESSAGE, OLE_SETCLIPBOARD, 0)
|
||||
}
|
||||
func GetFileInfo(sys interface{}) (uint32, []byte, uint32, uint32) {
|
||||
f := sys.(*syscall.Win32FileAttributeData)
|
||||
b := &bytes.Buffer{}
|
||||
core.WriteUInt32LE(f.LastWriteTime.LowDateTime, b)
|
||||
core.WriteUInt32LE(f.LastWriteTime.HighDateTime, b)
|
||||
return f.FileAttributes, b.Bytes(), f.FileSizeHigh, f.FileSizeLow
|
||||
}
|
||||
|
||||
func GetFileNames() []string {
|
||||
o := OleGetClipboard()
|
||||
var formatEtc FORMATETC
|
||||
var stgMedium STGMEDIUM
|
||||
formatEtc.CFormat = CF_HDROP
|
||||
formatEtc.Tymed = TYMED_HGLOBAL
|
||||
formatEtc.Aspect = DVASPECT_CONTENT
|
||||
formatEtc.Index = -1
|
||||
o.GetData(&formatEtc, &stgMedium)
|
||||
b, _ := stgMedium.Bytes()
|
||||
//DROPFILES
|
||||
r := bytes.NewReader(b[20:])
|
||||
fs := make([]string, 0, 20)
|
||||
for r.Len() > 0 {
|
||||
bs := make([]uint16, 0, 20)
|
||||
ln := r.Len()
|
||||
for j := 0; j < ln; j++ {
|
||||
b, _ := core.ReadUint16LE(r)
|
||||
if b == 0 {
|
||||
break
|
||||
}
|
||||
bs = append(bs, b)
|
||||
}
|
||||
name := string(utf16.Decode(bs))
|
||||
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
fs = append(fs, name)
|
||||
}
|
||||
|
||||
return fs
|
||||
}
|
||||
|
||||
const (
|
||||
/* File attribute flags */
|
||||
FILE_SHARE_READ = 0x00000001
|
||||
FILE_SHARE_WRITE = 0x00000002
|
||||
FILE_SHARE_DELETE = 0x00000004
|
||||
|
||||
FILE_ATTRIBUTE_READONLY = 0x00000001
|
||||
FILE_ATTRIBUTE_HIDDEN = 0x00000002
|
||||
FILE_ATTRIBUTE_SYSTEM = 0x00000004
|
||||
FILE_ATTRIBUTE_DIRECTORY = 0x00000010
|
||||
FILE_ATTRIBUTE_ARCHIVE = 0x00000020
|
||||
FILE_ATTRIBUTE_DEVICE = 0x00000040
|
||||
FILE_ATTRIBUTE_NORMAL = 0x00000080
|
||||
FILE_ATTRIBUTE_TEMPORARY = 0x00000100
|
||||
FILE_ATTRIBUTE_SPARSE_FILE = 0x00000200
|
||||
FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400
|
||||
FILE_ATTRIBUTE_COMPRESSED = 0x00000800
|
||||
FILE_ATTRIBUTE_OFFLINE = 0x00001000
|
||||
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000
|
||||
FILE_ATTRIBUTE_ENCRYPTED = 0x00004000
|
||||
FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x00008000
|
||||
FILE_ATTRIBUTE_VIRTUAL = 0x00010000
|
||||
FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x00020000
|
||||
FILE_ATTRIBUTE_EA = 0x00040000
|
||||
)
|
||||
|
||||
type DROPFILES struct {
|
||||
pFiles uintptr
|
||||
pt uintptr
|
||||
fNC bool
|
||||
fWide bool
|
||||
}
|
||||
864
grdp/plugin/cliprdr/data_object.go
Normal file
864
grdp/plugin/cliprdr/data_object.go
Normal file
@@ -0,0 +1,864 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
// dataobject.go
|
||||
package cliprdr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"ShotRDP/grdp/core"
|
||||
"ShotRDP/grdp/glog"
|
||||
"ShotRDP/grdp/win"
|
||||
)
|
||||
|
||||
const (
|
||||
S_OK = 0x00000000
|
||||
E_UNEXPECTED = 0x8000FFFF
|
||||
E_NOTIMPL = 0x80004001
|
||||
E_OUTOFMEMORY = 0x8007000E
|
||||
E_INVALIDARG = 0x80070057
|
||||
E_NOINTERFACE = 0x80004002
|
||||
E_POINTER = 0x80004003
|
||||
E_HANDLE = 0x80070006
|
||||
E_ABORT = 0x80004004
|
||||
E_FAIL = 0x80004005
|
||||
E_ACCESSDENIED = 0x80070005
|
||||
E_PENDING = 0x8000000A
|
||||
E_ADVISENOTSUPPORTED = 0x80040003
|
||||
E_FORMATETC = 0x80040064
|
||||
CO_E_CLASSSTRING = 0x800401F3
|
||||
)
|
||||
|
||||
type HRESULT uintptr
|
||||
|
||||
func (hr HRESULT) Error() string {
|
||||
switch uint32(hr) {
|
||||
case 0x80040064:
|
||||
return "DV_E_FORMATETC (0x80040064)"
|
||||
case 0x800401D3:
|
||||
return "CLIPBRD_E_BAD_DATA (0x800401D3)"
|
||||
case 0x80004005:
|
||||
return "E_FAIL (0x80004005)"
|
||||
case 0x00000001:
|
||||
return "S_FALSE (0x00000001)"
|
||||
}
|
||||
return fmt.Sprintf("%d", hr)
|
||||
}
|
||||
|
||||
const (
|
||||
TYMED_NULL = 0x0
|
||||
TYMED_HGLOBAL = 0x1
|
||||
TYMED_FILE = 0x2
|
||||
TYMED_ISTREAM = 0x4
|
||||
TYMED_ISTORAGE = 0x8
|
||||
TYMED_GDI = 0x10
|
||||
TYMED_MFPICT = 0x20
|
||||
TYMED_ENHMF = 0x40
|
||||
)
|
||||
|
||||
type iUnknownVtbl struct {
|
||||
QueryInterface uintptr
|
||||
AddRef uintptr
|
||||
Release uintptr
|
||||
}
|
||||
type iDataObjectVtbl struct {
|
||||
iUnknownVtbl
|
||||
GetData uintptr
|
||||
GetDataHere uintptr
|
||||
QueryGetData uintptr
|
||||
GetCanonicalFormatEtc uintptr
|
||||
SetData uintptr
|
||||
EnumFormatEtc uintptr
|
||||
DAdvise uintptr
|
||||
DUnadvise uintptr
|
||||
EnumDAdvise uintptr
|
||||
}
|
||||
|
||||
type FORMATETC struct {
|
||||
CFormat uint32
|
||||
DvTargetDevice uintptr
|
||||
Aspect uint32
|
||||
Index int32
|
||||
Tymed uint32
|
||||
}
|
||||
type STGMEDIUM struct {
|
||||
Tymed uint32
|
||||
UnionMember uintptr
|
||||
PUnkForRelease *IUnknown
|
||||
}
|
||||
type ISequentialStreamVtbl struct {
|
||||
iUnknownVtbl
|
||||
Read uintptr
|
||||
Write uintptr
|
||||
}
|
||||
|
||||
type IUnknown struct {
|
||||
vtbl iUnknownVtbl
|
||||
}
|
||||
|
||||
func (obj *IUnknown) Release() error {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if ret != 0 {
|
||||
return HRESULT(ret)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m STGMEDIUM) Release() {
|
||||
if m.PUnkForRelease == nil {
|
||||
win.ReleaseStgMedium((*win.STGMEDIUM)(unsafe.Pointer(&m)))
|
||||
}
|
||||
}
|
||||
|
||||
func (m STGMEDIUM) Stream() (*IStream, error) {
|
||||
if m.Tymed != TYMED_ISTREAM {
|
||||
return nil, fmt.Errorf("invalid Tymed")
|
||||
}
|
||||
return (*IStream)(unsafe.Pointer(m.UnionMember)), nil
|
||||
}
|
||||
|
||||
func (m STGMEDIUM) Bytes() ([]byte, error) {
|
||||
if m.Tymed != TYMED_HGLOBAL {
|
||||
return nil, fmt.Errorf("invalid Tymed")
|
||||
}
|
||||
size := GlobalSize(m.UnionMember)
|
||||
l := GlobalLock(m.UnionMember)
|
||||
defer GlobalUnlock(m.UnionMember)
|
||||
|
||||
result := make([]byte, size)
|
||||
win.RtlCopyMemory(uintptr(unsafe.Pointer(&result[0])), uintptr(unsafe.Pointer(l)), size)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type IDataObject struct {
|
||||
vtbl *iDataObjectVtbl
|
||||
}
|
||||
|
||||
func (obj *IDataObject) Release() error {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if ret != 0 {
|
||||
return HRESULT(ret)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (obj *IDataObject) GetData(formatEtc *FORMATETC, medium *STGMEDIUM) error {
|
||||
s2 := unsafe.Sizeof(*medium)
|
||||
_ = s2
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.GetData,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(formatEtc)),
|
||||
uintptr(unsafe.Pointer(medium)),
|
||||
)
|
||||
|
||||
if ret != 0 {
|
||||
return HRESULT(ret)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (obj *IDataObject) GetDataHere(formatEtc *FORMATETC, medium *STGMEDIUM) error {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.GetDataHere,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(formatEtc)),
|
||||
uintptr(unsafe.Pointer(medium)),
|
||||
)
|
||||
|
||||
if ret != 0 {
|
||||
return HRESULT(ret)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (obj *IDataObject) QueryGetData(formatEtc *FORMATETC) error {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.QueryGetData,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(formatEtc)),
|
||||
0,
|
||||
)
|
||||
|
||||
if ret != 0 {
|
||||
return HRESULT(ret)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (obj *IDataObject) EnumFormatEtc(direction uint32, pIEnumFORMATETC **IEnumFORMATETC) error {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.EnumFormatEtc,
|
||||
3,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(direction),
|
||||
uintptr(unsafe.Pointer(pIEnumFORMATETC)),
|
||||
)
|
||||
|
||||
if ret != 0 {
|
||||
return HRESULT(ret)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type iEnumFORMATETCVtbl struct {
|
||||
iUnknownVtbl
|
||||
Next uintptr
|
||||
Skip uintptr
|
||||
Reset uintptr
|
||||
Clone uintptr
|
||||
}
|
||||
|
||||
type IEnumFORMATETC struct {
|
||||
vtbl *iEnumFORMATETCVtbl
|
||||
}
|
||||
|
||||
func (obj *IEnumFORMATETC) Release() error {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if ret != 0 {
|
||||
return HRESULT(ret)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (obj *IEnumFORMATETC) Next(formatEtc []FORMATETC, celtFetched *uint32) error {
|
||||
ret, _, _ := syscall.Syscall6(
|
||||
obj.vtbl.Next,
|
||||
4,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(len(formatEtc)),
|
||||
uintptr(unsafe.Pointer(&formatEtc[0])),
|
||||
uintptr(unsafe.Pointer(celtFetched)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if ret != 1 {
|
||||
return HRESULT(ret)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type EnumInstance struct {
|
||||
iEnumFORMATETC IEnumFORMATETC
|
||||
refCount int32
|
||||
index int
|
||||
formatEtc []FORMATETC
|
||||
}
|
||||
|
||||
var (
|
||||
IID_IDataObject = win.GUID{0x10e, 0, 0, [8]byte{0xc0, 0, 0, 0, 0, 0, 0, 0x46}}
|
||||
IID_IUnknown = win.GUID{0x000, 0, 0, [8]byte{0xc0, 0, 0, 0, 0, 0, 0, 0x46}}
|
||||
IID_IEnumFORMATETC = win.GUID{0x103, 0, 0, [8]byte{0xc0, 0, 0, 0, 0, 0, 0, 0x46}}
|
||||
IID_IStream = win.GUID{0x00c, 0, 0, [8]byte{0xc0, 0, 0, 0, 0, 0, 0, 0x46}}
|
||||
)
|
||||
|
||||
func newEnumInstance(fs []FORMATETC) *EnumInstance {
|
||||
var instance EnumInstance
|
||||
ie := &instance.iEnumFORMATETC
|
||||
ie.vtbl = new(iEnumFORMATETCVtbl)
|
||||
ie.vtbl.QueryInterface = syscall.NewCallback((*EnumInstance).QueryInterface)
|
||||
ie.vtbl.AddRef = syscall.NewCallback((*EnumInstance).AddRef)
|
||||
ie.vtbl.Release = syscall.NewCallback((*EnumInstance).Release)
|
||||
ie.vtbl.Next = syscall.NewCallback((*EnumInstance).Next)
|
||||
ie.vtbl.Skip = syscall.NewCallback((*EnumInstance).Skip)
|
||||
ie.vtbl.Reset = syscall.NewCallback((*EnumInstance).Reset)
|
||||
ie.vtbl.Clone = syscall.NewCallback((*EnumInstance).Clone)
|
||||
|
||||
instance.refCount = 1
|
||||
if len(fs) > 0 {
|
||||
instance.formatEtc = make([]FORMATETC, len(fs))
|
||||
copy(instance.formatEtc, fs)
|
||||
}
|
||||
|
||||
return &instance
|
||||
}
|
||||
|
||||
func (i *EnumInstance) QueryInterface(riid win.REFGUID, ppvObject *uintptr) uintptr {
|
||||
if win.IsEqualGUID(riid, &IID_IEnumFORMATETC) ||
|
||||
win.IsEqualGUID(riid, &IID_IUnknown) {
|
||||
*ppvObject = uintptr(unsafe.Pointer(i))
|
||||
i.AddRef()
|
||||
return 0
|
||||
}
|
||||
*ppvObject = 0
|
||||
return E_NOINTERFACE
|
||||
}
|
||||
|
||||
func (i *EnumInstance) AddRef() uintptr {
|
||||
n := atomic.AddInt32(&i.refCount, 1)
|
||||
return uintptr(n)
|
||||
}
|
||||
|
||||
func (i *EnumInstance) Release() uintptr {
|
||||
n := atomic.AddInt32(&i.refCount, -1)
|
||||
if n == 0 {
|
||||
i = nil
|
||||
return 0
|
||||
}
|
||||
return uintptr(n)
|
||||
}
|
||||
|
||||
func (i *EnumInstance) Next(celt uint32, rgelt *FORMATETC, pceltFetched *uint32) uintptr {
|
||||
r := make([]FORMATETC, celt)
|
||||
var idx uint32
|
||||
for i.index < len(i.formatEtc) && idx < celt {
|
||||
r[idx] = i.formatEtc[i.index]
|
||||
i.index++
|
||||
idx++
|
||||
}
|
||||
|
||||
*rgelt = r[0]
|
||||
if pceltFetched != nil {
|
||||
*pceltFetched = idx
|
||||
}
|
||||
|
||||
if idx != celt {
|
||||
return E_FAIL
|
||||
}
|
||||
return 0
|
||||
}
|
||||
func (i *EnumInstance) Skip(celt uint32) uintptr {
|
||||
if i.index+int(celt) > len(i.formatEtc) {
|
||||
return E_FAIL
|
||||
}
|
||||
i.index += int(celt)
|
||||
return 0
|
||||
}
|
||||
func (i *EnumInstance) Reset() uintptr {
|
||||
i.index = 0
|
||||
return 0
|
||||
}
|
||||
func (i *EnumInstance) Clone(ppEnum **IEnumFORMATETC) uintptr {
|
||||
ins := newEnumInstance(i.formatEtc)
|
||||
ins.index = i.index
|
||||
(*ppEnum) = (*IEnumFORMATETC)(unsafe.Pointer(ins))
|
||||
|
||||
return 0
|
||||
}
|
||||
func CreateDataObject(c *CliprdrClient) *IDataObject {
|
||||
fmtetc := make([]FORMATETC, 2)
|
||||
stgmeds := make([]STGMEDIUM, 2)
|
||||
|
||||
fmtetc[0].CFormat = RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW)
|
||||
fmtetc[0].Aspect = DVASPECT_CONTENT
|
||||
fmtetc[0].Index = 0
|
||||
//fmtetc[0].DvTargetDevice = nil
|
||||
fmtetc[0].Tymed = TYMED_HGLOBAL
|
||||
stgmeds[0].Tymed = TYMED_HGLOBAL
|
||||
//stgmeds[0].UnionMember = nil
|
||||
stgmeds[0].PUnkForRelease = nil
|
||||
fmtetc[1].CFormat = RegisterClipboardFormat(CFSTR_FILECONTENTS)
|
||||
fmtetc[1].Aspect = DVASPECT_CONTENT
|
||||
fmtetc[1].Index = 0
|
||||
//fmtetc[1].DvTargetDevice = nil
|
||||
fmtetc[1].Tymed = TYMED_ISTREAM
|
||||
stgmeds[1].Tymed = TYMED_ISTREAM
|
||||
//stgmeds[1].UnionMember = nil
|
||||
stgmeds[1].PUnkForRelease = nil
|
||||
|
||||
instance := newDataInstance()
|
||||
instance.refCount = 1
|
||||
instance.formatEtc = fmtetc
|
||||
instance.stgMedium = stgmeds
|
||||
instance.data = c
|
||||
|
||||
return (*IDataObject)(unsafe.Pointer(instance))
|
||||
}
|
||||
|
||||
type DataInstance struct {
|
||||
iDataObject IDataObject
|
||||
refCount int32
|
||||
idx int32
|
||||
formatEtc []FORMATETC
|
||||
stgMedium []STGMEDIUM
|
||||
streams []*StreamInstance
|
||||
data interface{}
|
||||
}
|
||||
|
||||
func newDataInstance() *DataInstance {
|
||||
var instance DataInstance
|
||||
obj := &instance.iDataObject
|
||||
obj.vtbl = new(iDataObjectVtbl)
|
||||
obj.vtbl.QueryInterface = syscall.NewCallback((*DataInstance).QueryInterface)
|
||||
obj.vtbl.AddRef = syscall.NewCallback((*DataInstance).AddRef)
|
||||
obj.vtbl.Release = syscall.NewCallback((*DataInstance).Release)
|
||||
obj.vtbl.GetData = syscall.NewCallback((*DataInstance).GetData)
|
||||
obj.vtbl.GetDataHere = syscall.NewCallback((*DataInstance).GetDataHere)
|
||||
obj.vtbl.QueryGetData = syscall.NewCallback((*DataInstance).QueryGetData)
|
||||
obj.vtbl.GetCanonicalFormatEtc = syscall.NewCallback((*DataInstance).GetCanonicalFormatEtc)
|
||||
obj.vtbl.SetData = syscall.NewCallback((*DataInstance).SetData)
|
||||
obj.vtbl.EnumFormatEtc = syscall.NewCallback((*DataInstance).EnumFormatEtc)
|
||||
obj.vtbl.DAdvise = syscall.NewCallback((*DataInstance).DAdvise)
|
||||
obj.vtbl.DUnadvise = syscall.NewCallback((*DataInstance).DUnadvise)
|
||||
obj.vtbl.EnumDAdvise = syscall.NewCallback((*DataInstance).EnumDAdvise)
|
||||
|
||||
return &instance
|
||||
}
|
||||
|
||||
func (i *DataInstance) QueryInterface(riid win.REFGUID, ppvObject *uintptr) uintptr {
|
||||
if win.IsEqualGUID(riid, &IID_IDataObject) ||
|
||||
win.IsEqualGUID(riid, &IID_IUnknown) {
|
||||
*ppvObject = uintptr(unsafe.Pointer(i))
|
||||
i.AddRef()
|
||||
return 0
|
||||
}
|
||||
|
||||
*ppvObject = 0
|
||||
return E_NOINTERFACE
|
||||
}
|
||||
|
||||
func (i *DataInstance) AddRef() uintptr {
|
||||
n := atomic.AddInt32(&i.refCount, 1)
|
||||
return uintptr(n)
|
||||
}
|
||||
|
||||
func (i *DataInstance) Release() uintptr {
|
||||
n := atomic.AddInt32(&i.refCount, -1)
|
||||
if n == 0 {
|
||||
i = nil
|
||||
return 0
|
||||
}
|
||||
return uintptr(n)
|
||||
}
|
||||
|
||||
func (i *DataInstance) GetData(formatEtc *FORMATETC, medium *STGMEDIUM) uintptr {
|
||||
idx := -1
|
||||
for j, f := range i.formatEtc {
|
||||
if formatEtc.Tymed&f.Tymed != 0 &&
|
||||
formatEtc.CFormat == f.CFormat &&
|
||||
formatEtc.Aspect&f.Aspect != 0 {
|
||||
idx = j
|
||||
}
|
||||
}
|
||||
if idx == -1 {
|
||||
return E_FORMATETC
|
||||
}
|
||||
glog.Debugf("GetData:%+v, %s", formatEtc.CFormat, GetClipboardFormatName(formatEtc.CFormat))
|
||||
|
||||
medium.Tymed = i.formatEtc[idx].Tymed
|
||||
|
||||
if i.formatEtc[idx].CFormat == RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW) {
|
||||
c := i.data.(*CliprdrClient)
|
||||
if remoteid, ok := c.formatIdMap[i.formatEtc[idx].CFormat]; ok {
|
||||
c.sendFormatDataRequest(remoteid)
|
||||
b := <-c.reply
|
||||
if len(b) == 0 {
|
||||
return E_FAIL
|
||||
}
|
||||
medium.UnionMember = HmemAlloc(b)
|
||||
var dsc FileGroupDescriptor
|
||||
dsc.Unpack(b)
|
||||
if dsc.CItems > 0 {
|
||||
glog.Debug("Items:", dsc.CItems)
|
||||
i.streams = make([]*StreamInstance, dsc.CItems)
|
||||
var j uint32
|
||||
for j = 0; j < dsc.CItems; j++ {
|
||||
glog.Debug("FileName:", core.UnicodeDecode(dsc.Fgd[j].FileName))
|
||||
s := newStream(j, i.data, &dsc.Fgd[j])
|
||||
i.streams[j] = s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if i.formatEtc[idx].CFormat == RegisterClipboardFormat(CFSTR_FILECONTENTS) {
|
||||
if formatEtc.Index >= 0 && formatEtc.Index < int32(len(i.streams)) {
|
||||
medium.UnionMember = uintptr(unsafe.Pointer(i.streams[formatEtc.Index]))
|
||||
i.AddRef()
|
||||
} else {
|
||||
return E_INVALIDARG
|
||||
}
|
||||
} else {
|
||||
return E_UNEXPECTED
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (i *DataInstance) GetDataHere(formatEtc *FORMATETC, medium *STGMEDIUM) uintptr {
|
||||
return E_NOTIMPL
|
||||
}
|
||||
|
||||
func (i *DataInstance) QueryGetData(formatEtc *FORMATETC) uintptr {
|
||||
for _, f := range i.formatEtc {
|
||||
if formatEtc.Tymed&f.Tymed != 0 &&
|
||||
formatEtc.CFormat == f.CFormat &&
|
||||
formatEtc.Aspect&f.Aspect != 0 {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
return E_FORMATETC
|
||||
}
|
||||
|
||||
func (i *DataInstance) GetCanonicalFormatEtc(informatEtc, outformatEtc *FORMATETC) uintptr {
|
||||
return E_NOTIMPL
|
||||
}
|
||||
|
||||
func (i *DataInstance) SetData(formatEtc *FORMATETC, medium *STGMEDIUM, r bool) uintptr {
|
||||
return E_NOTIMPL
|
||||
}
|
||||
|
||||
func (i *DataInstance) EnumFormatEtc(dwDirection uint32, ppenumFormatEtc **IEnumFORMATETC) uintptr {
|
||||
if dwDirection == 1 {
|
||||
ins := newEnumInstance(i.formatEtc)
|
||||
(*ppenumFormatEtc) = (*IEnumFORMATETC)(unsafe.Pointer(ins))
|
||||
return 0
|
||||
}
|
||||
|
||||
return E_NOTIMPL
|
||||
}
|
||||
|
||||
func (i *DataInstance) DAdvise(formatEtc *FORMATETC, advf uint32, pAdvSink uintptr, pdwConnection *uint32) uintptr {
|
||||
return E_ADVISENOTSUPPORTED
|
||||
}
|
||||
func (i *DataInstance) DUnadvise(dwDirection uint32) uintptr {
|
||||
return E_ADVISENOTSUPPORTED
|
||||
}
|
||||
func (i *DataInstance) EnumDAdvise(ppenumAdvise uintptr) uintptr {
|
||||
return E_ADVISENOTSUPPORTED
|
||||
}
|
||||
|
||||
const (
|
||||
STREAM_SEEK_SET = 0
|
||||
STREAM_SEEK_CUR = 1
|
||||
STREAM_SEEK_END = 2
|
||||
)
|
||||
|
||||
const (
|
||||
STATFLAG_DEFAULT = 0
|
||||
STATFLAG_NONAME = 1
|
||||
STATFLAG_NOOPEN = 2
|
||||
)
|
||||
|
||||
const (
|
||||
STG_E_INSUFFICIENTMEMORY = 0x80030008
|
||||
STG_E_INVALIDFLAG = 0x800300FF
|
||||
)
|
||||
|
||||
const (
|
||||
STGTY_STORAGE = 1
|
||||
STGTY_STREAM = 2
|
||||
STGTY_LOCKBYTES = 3
|
||||
STGTY_PROPERTY = 4
|
||||
)
|
||||
|
||||
const (
|
||||
LOCK_WRITE = 1
|
||||
LOCK_EXCLUSIVE = 2
|
||||
LOCK_ONLYONCE = 4
|
||||
)
|
||||
|
||||
const (
|
||||
GENERIC_READ = 0x80000000
|
||||
GENERIC_WRITE = 0x40000000
|
||||
GENERIC_EXECUTE = 0x20000000
|
||||
)
|
||||
|
||||
type IStreamVtbl struct {
|
||||
ISequentialStreamVtbl
|
||||
Seek uintptr
|
||||
SetSize uintptr
|
||||
CopyTo uintptr
|
||||
Commit uintptr
|
||||
Revert uintptr
|
||||
LockRegion uintptr
|
||||
UnlockRegion uintptr
|
||||
Stat uintptr
|
||||
Clone uintptr
|
||||
}
|
||||
|
||||
type IStream struct {
|
||||
vtbl *IStreamVtbl
|
||||
}
|
||||
type ULARGE_INTEGER struct {
|
||||
QuadPart uint64
|
||||
}
|
||||
type LARGE_INTEGER struct {
|
||||
QuadPart int64
|
||||
}
|
||||
|
||||
func (l *ULARGE_INTEGER) LowPart() *uint32 {
|
||||
return (*uint32)(unsafe.Pointer(&l.QuadPart))
|
||||
}
|
||||
func (l *ULARGE_INTEGER) HighPart() *uint32 {
|
||||
return (*uint32)(unsafe.Pointer(uintptr(unsafe.Pointer(&l.QuadPart)) + uintptr(4)))
|
||||
}
|
||||
func (l *LARGE_INTEGER) LowPart() *uint32 {
|
||||
return (*uint32)(unsafe.Pointer(&l.QuadPart))
|
||||
}
|
||||
func (l *LARGE_INTEGER) HighPart() *int32 {
|
||||
return (*int32)(unsafe.Pointer(uintptr(unsafe.Pointer(&l.QuadPart)) + uintptr(4)))
|
||||
}
|
||||
|
||||
type StreamInstance struct {
|
||||
iStream IStream
|
||||
streamId uint32
|
||||
refCount int32
|
||||
index uint32
|
||||
lSize ULARGE_INTEGER
|
||||
lOffset ULARGE_INTEGER
|
||||
dsc FileDescriptor
|
||||
data interface{}
|
||||
}
|
||||
type STATSTG struct {
|
||||
pwcsName []uint16
|
||||
sType uint32
|
||||
cbSize ULARGE_INTEGER
|
||||
mtime win.FILETIME
|
||||
ctime win.FILETIME
|
||||
atime win.FILETIME
|
||||
grfMode uint32
|
||||
grfLocksSupported uint32
|
||||
clsid win.CLSID
|
||||
grfStateBits uint32
|
||||
reserved uint32
|
||||
}
|
||||
|
||||
func (obj *IStream) Read(buffer []byte) (int, error) {
|
||||
bufPtr := &buffer[0]
|
||||
var read uint32
|
||||
ret, _, _ := syscall.Syscall6(
|
||||
obj.vtbl.Read,
|
||||
4,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
uintptr(unsafe.Pointer(bufPtr)),
|
||||
uintptr(len(buffer)),
|
||||
uintptr(unsafe.Pointer(&read)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if ret == 1 {
|
||||
return int(read), io.EOF
|
||||
}
|
||||
if ret != 0 {
|
||||
return int(read), HRESULT(ret)
|
||||
}
|
||||
return int(read), nil
|
||||
}
|
||||
func (obj *IStream) Close() error {
|
||||
return obj.Release()
|
||||
}
|
||||
func (obj *IStream) Release() error {
|
||||
ret, _, _ := syscall.Syscall(
|
||||
obj.vtbl.Release,
|
||||
1,
|
||||
uintptr(unsafe.Pointer(obj)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
if ret != 0 {
|
||||
return HRESULT(ret)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func newStream(index uint32, data interface{}, dsc *FileDescriptor) *StreamInstance {
|
||||
var instance StreamInstance
|
||||
is := &instance.iStream
|
||||
is.vtbl = new(IStreamVtbl)
|
||||
is.vtbl.QueryInterface = syscall.NewCallback((*StreamInstance).QueryInterface)
|
||||
is.vtbl.AddRef = syscall.NewCallback((*StreamInstance).AddRef)
|
||||
is.vtbl.Release = syscall.NewCallback((*StreamInstance).Release)
|
||||
is.vtbl.Read = syscall.NewCallback((*StreamInstance).Read)
|
||||
is.vtbl.Write = syscall.NewCallback((*StreamInstance).Write)
|
||||
is.vtbl.Seek = syscall.NewCallback((*StreamInstance).Seek)
|
||||
is.vtbl.SetSize = syscall.NewCallback((*StreamInstance).SetSize)
|
||||
is.vtbl.CopyTo = syscall.NewCallback((*StreamInstance).CopyTo)
|
||||
is.vtbl.Commit = syscall.NewCallback((*StreamInstance).Commit)
|
||||
is.vtbl.Revert = syscall.NewCallback((*StreamInstance).Revert)
|
||||
is.vtbl.LockRegion = syscall.NewCallback((*StreamInstance).LockRegion)
|
||||
is.vtbl.UnlockRegion = syscall.NewCallback((*StreamInstance).UnlockRegion)
|
||||
is.vtbl.Stat = syscall.NewCallback((*StreamInstance).Stat)
|
||||
is.vtbl.Clone = syscall.NewCallback((*StreamInstance).Clone)
|
||||
|
||||
instance.streamId = (uint32)(reflect.ValueOf(&instance).Pointer())
|
||||
instance.refCount = 1
|
||||
instance.dsc = *dsc
|
||||
instance.data = data
|
||||
instance.index = index
|
||||
if !instance.dsc.hasFileSize() && !instance.dsc.isDir() {
|
||||
c := data.(*CliprdrClient)
|
||||
var r CliprdrFileContentsRequest
|
||||
r.StreamId = instance.streamId
|
||||
r.Lindex = instance.index
|
||||
r.DwFlags = FILECONTENTS_SIZE
|
||||
r.CbRequested = 8
|
||||
c.sendFormatContentsRequest(r)
|
||||
b := <-c.reply
|
||||
instance.lSize.QuadPart = core.BytesToUint64(b)
|
||||
} else {
|
||||
b := &bytes.Buffer{}
|
||||
core.WriteUInt32LE(dsc.FileSizeLow, b)
|
||||
core.WriteUInt32LE(dsc.FileSizeHigh, b)
|
||||
instance.lSize.QuadPart = core.BytesToUint64(b.Bytes())
|
||||
}
|
||||
|
||||
return &instance
|
||||
}
|
||||
|
||||
func (i *StreamInstance) QueryInterface(riid win.REFGUID, ppvObject *uintptr) uintptr {
|
||||
if win.IsEqualGUID(riid, &IID_IStream) ||
|
||||
win.IsEqualGUID(riid, &IID_IUnknown) {
|
||||
*ppvObject = uintptr(unsafe.Pointer(i))
|
||||
i.AddRef()
|
||||
return 0
|
||||
}
|
||||
*ppvObject = 0
|
||||
return E_NOINTERFACE
|
||||
}
|
||||
|
||||
func (i *StreamInstance) AddRef() uintptr {
|
||||
n := atomic.AddInt32(&i.refCount, 1)
|
||||
return uintptr(n)
|
||||
}
|
||||
|
||||
func (i *StreamInstance) Release() uintptr {
|
||||
n := atomic.AddInt32(&i.refCount, -1)
|
||||
if n == 0 {
|
||||
i = nil
|
||||
return 0
|
||||
}
|
||||
return uintptr(n)
|
||||
}
|
||||
|
||||
func (i *StreamInstance) Read(pv uintptr, cb uint32, cbRead *uint32) uintptr {
|
||||
glog.Debug("StreamInstance Read:", i.lOffset.QuadPart, i.lSize.QuadPart)
|
||||
if i.lOffset.QuadPart >= i.lSize.QuadPart {
|
||||
return 1
|
||||
}
|
||||
|
||||
c := i.data.(*CliprdrClient)
|
||||
*cbRead = 0
|
||||
var r CliprdrFileContentsRequest
|
||||
r.StreamId = i.streamId
|
||||
r.Lindex = i.index
|
||||
r.DwFlags = FILECONTENTS_RANGE
|
||||
r.NPositionHigh = *(i.lOffset.HighPart())
|
||||
r.NPositionLow = *(i.lOffset.LowPart())
|
||||
r.CbRequested = cb
|
||||
c.sendFormatContentsRequest(r)
|
||||
b := <-c.reply
|
||||
if len(b) == 0 {
|
||||
return E_FAIL
|
||||
}
|
||||
win.RtlCopyMemory(pv, uintptr(unsafe.Pointer(&b[0])), win.SIZE_T(len(b)))
|
||||
*cbRead = uint32(len(b))
|
||||
i.lOffset.QuadPart += uint64(len(b))
|
||||
glog.Debug("StreamInstance Read:", *cbRead, cb)
|
||||
if *cbRead < cb {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (i *StreamInstance) Write(pv uintptr, cb uint32, cbWritten *uint32) uintptr {
|
||||
return E_ACCESSDENIED
|
||||
}
|
||||
|
||||
func (i *StreamInstance) Seek(dlibMove LARGE_INTEGER, dwOrigin uint32, plibNewPosition *ULARGE_INTEGER) uintptr {
|
||||
glog.Debug("StreamInstance Seek:", dwOrigin, dlibMove, plibNewPosition)
|
||||
var newoffset uint64 = i.lOffset.QuadPart
|
||||
switch dwOrigin {
|
||||
case STREAM_SEEK_SET:
|
||||
newoffset = uint64(dlibMove.QuadPart)
|
||||
break
|
||||
|
||||
case STREAM_SEEK_CUR:
|
||||
newoffset += uint64(dlibMove.QuadPart)
|
||||
break
|
||||
|
||||
case STREAM_SEEK_END:
|
||||
newoffset = i.lSize.QuadPart + uint64(dlibMove.QuadPart)
|
||||
break
|
||||
|
||||
default:
|
||||
return E_INVALIDARG
|
||||
}
|
||||
glog.Debug("StreamInstance Seek:", newoffset, i.lSize.QuadPart)
|
||||
if newoffset < 0 || newoffset >= i.lSize.QuadPart {
|
||||
return 1
|
||||
}
|
||||
i.lOffset.QuadPart = newoffset
|
||||
|
||||
if plibNewPosition != nil {
|
||||
(*plibNewPosition).QuadPart = i.lOffset.QuadPart
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (i *StreamInstance) SetSize(libNewSize ULARGE_INTEGER) uintptr {
|
||||
return E_NOTIMPL
|
||||
}
|
||||
|
||||
func (i *StreamInstance) CopyTo(pstm *IStream, cb ULARGE_INTEGER, cbRead, cbWritten *ULARGE_INTEGER) uintptr {
|
||||
return E_NOTIMPL
|
||||
}
|
||||
|
||||
func (i *StreamInstance) Commit(grfCommitFlags uint32) uintptr {
|
||||
return E_NOTIMPL
|
||||
}
|
||||
|
||||
func (i *StreamInstance) Revert() uintptr {
|
||||
return E_NOTIMPL
|
||||
}
|
||||
|
||||
func (i *StreamInstance) LockRegion(libOffset, cb ULARGE_INTEGER, dwLockType uint32) uintptr {
|
||||
return E_NOTIMPL
|
||||
}
|
||||
|
||||
func (i *StreamInstance) UnlockRegion(libOffset, cb ULARGE_INTEGER, dwLockType uint32) uintptr {
|
||||
return E_NOTIMPL
|
||||
}
|
||||
|
||||
func (i *StreamInstance) Stat(pstatstg *STATSTG, grfStatFlag uint32) uintptr {
|
||||
switch grfStatFlag {
|
||||
case STATFLAG_DEFAULT:
|
||||
return STG_E_INSUFFICIENTMEMORY
|
||||
|
||||
case STATFLAG_NONAME:
|
||||
pstatstg.cbSize.QuadPart = i.lSize.QuadPart
|
||||
pstatstg.grfLocksSupported = LOCK_EXCLUSIVE
|
||||
pstatstg.grfMode = GENERIC_READ
|
||||
pstatstg.grfStateBits = 0
|
||||
pstatstg.sType = STGTY_STREAM
|
||||
break
|
||||
|
||||
case STATFLAG_NOOPEN:
|
||||
return STG_E_INVALIDFLAG
|
||||
|
||||
default:
|
||||
return STG_E_INVALIDFLAG
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (i *StreamInstance) Clone(ppstm **IStream) uintptr {
|
||||
return E_NOTIMPL
|
||||
}
|
||||
162
grdp/plugin/drdynvc/dvc.go
Normal file
162
grdp/plugin/drdynvc/dvc.go
Normal file
@@ -0,0 +1,162 @@
|
||||
package drdynvc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
|
||||
"ShotRDP/grdp/core"
|
||||
"ShotRDP/grdp/glog"
|
||||
"ShotRDP/grdp/plugin"
|
||||
)
|
||||
|
||||
const (
|
||||
ChannelName = plugin.DRDYNVC_SVC_CHANNEL_NAME
|
||||
ChannelOption = plugin.CHANNEL_OPTION_INITIALIZED |
|
||||
plugin.CHANNEL_OPTION_ENCRYPT_RDP
|
||||
)
|
||||
|
||||
const (
|
||||
MAX_DVC_CHANNELS = 20
|
||||
)
|
||||
|
||||
const (
|
||||
DYNVC_CREATE_REQ = 0x01
|
||||
DYNVC_DATA_FIRST = 0x02
|
||||
DYNVC_DATA = 0x03
|
||||
DYNVC_CLOSE = 0x04
|
||||
DYNVC_CAPABILITIES = 0x05
|
||||
DYNVC_DATA_FIRST_COMPRESSED = 0x06
|
||||
DYNVC_DATA_COMPRESSED = 0x07
|
||||
DYNVC_SOFT_SYNC_REQUEST = 0x08
|
||||
DYNVC_SOFT_SYNC_RESPONSE = 0x09
|
||||
)
|
||||
|
||||
type ChannelClient struct {
|
||||
name string
|
||||
id uint32
|
||||
channelSender core.ChannelSender
|
||||
}
|
||||
|
||||
type DvcClient struct {
|
||||
w core.ChannelSender
|
||||
channels map[string]ChannelClient
|
||||
}
|
||||
|
||||
func NewDvcClient() *DvcClient {
|
||||
return &DvcClient{
|
||||
channels: make(map[string]ChannelClient, 100),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DvcClient) LoadAddin(f core.ChannelSender) {
|
||||
|
||||
}
|
||||
|
||||
type DvcHeader struct {
|
||||
cmd uint8
|
||||
sp uint8
|
||||
cbChId uint8
|
||||
}
|
||||
|
||||
func readHeader(r io.Reader) *DvcHeader {
|
||||
value, _ := core.ReadUInt8(r)
|
||||
cmd := (value & 0xf0) >> 4
|
||||
sp := (value & 0x0c) >> 2
|
||||
cbChId := (value & 0x03) >> 0
|
||||
return &DvcHeader{cmd, sp, cbChId}
|
||||
}
|
||||
|
||||
func (h *DvcHeader) serialize(channelId uint32) []byte {
|
||||
b := &bytes.Buffer{}
|
||||
core.WriteUInt8((h.cmd<<4)|(h.sp<<2)|h.cbChId, b)
|
||||
if h.cbChId == 0 {
|
||||
core.WriteUInt8(uint8(channelId), b)
|
||||
} else if h.cbChId == 1 {
|
||||
core.WriteUInt16LE(uint16(channelId), b)
|
||||
} else {
|
||||
core.WriteUInt32LE(channelId, b)
|
||||
}
|
||||
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
func (c *DvcClient) Send(s []byte) (int, error) {
|
||||
glog.Debug("len:", len(s), "data:", hex.EncodeToString(s))
|
||||
name, _ := c.GetType()
|
||||
return c.w.SendToChannel(name, s)
|
||||
}
|
||||
func (c *DvcClient) Sender(f core.ChannelSender) {
|
||||
c.w = f
|
||||
}
|
||||
func (c *DvcClient) GetType() (string, uint32) {
|
||||
return ChannelName, ChannelOption
|
||||
}
|
||||
|
||||
func (c *DvcClient) Process(s []byte) {
|
||||
glog.Debug("recv:", hex.EncodeToString(s))
|
||||
r := bytes.NewReader(s)
|
||||
hdr := readHeader(r)
|
||||
glog.Infof("dvc: Cmd=0x%x, Sp=%d CbChId=%d all=%d", hdr.cmd, hdr.sp, hdr.cbChId, r.Len())
|
||||
|
||||
b, _ := core.ReadBytes(r.Len(), r)
|
||||
|
||||
switch hdr.cmd {
|
||||
case DYNVC_CAPABILITIES:
|
||||
glog.Info("DYNVC_CAPABILITIES")
|
||||
c.processCapsPdu(hdr, b)
|
||||
case DYNVC_CREATE_REQ:
|
||||
glog.Info("DYNVC_CREATE_REQ")
|
||||
c.processCreateReq(hdr, b)
|
||||
case DYNVC_DATA_FIRST:
|
||||
glog.Info("DYNVC_DATA_FIRST")
|
||||
case DYNVC_DATA:
|
||||
glog.Info("DYNVC_DATA")
|
||||
case DYNVC_CLOSE:
|
||||
glog.Info("DYNVC_CLOSE")
|
||||
default:
|
||||
glog.Errorf("type 0x%x not supported", hdr.cmd)
|
||||
}
|
||||
}
|
||||
func (c *DvcClient) processCreateReq(hdr *DvcHeader, s []byte) {
|
||||
r := bytes.NewReader(s)
|
||||
channelId := readDvcId(r, hdr.cbChId)
|
||||
name, _ := core.ReadBytes(r.Len(), r)
|
||||
channelName := string(name)
|
||||
glog.Infof("Server requests channelId=%d, name=%s", channelId, channelName)
|
||||
|
||||
//response
|
||||
b := &bytes.Buffer{}
|
||||
b.Write(hdr.serialize(channelId))
|
||||
core.WriteUInt32LE(0, b)
|
||||
c.Send(b.Bytes())
|
||||
}
|
||||
|
||||
func readDvcId(r io.Reader, cbLen uint8) (id uint32) {
|
||||
switch cbLen {
|
||||
case 0:
|
||||
i, _ := core.ReadUInt8(r)
|
||||
id = uint32(i)
|
||||
case 1:
|
||||
i, _ := core.ReadUint16LE(r)
|
||||
id = uint32(i)
|
||||
default:
|
||||
id, _ = core.ReadUInt32LE(r)
|
||||
}
|
||||
return
|
||||
}
|
||||
func (c *DvcClient) processCapsPdu(hdr *DvcHeader, s []byte) {
|
||||
r := bytes.NewReader(s)
|
||||
core.ReadUInt8(r)
|
||||
ver, _ := core.ReadUint16LE(r)
|
||||
glog.Infof("Server supports dvc=%d", ver)
|
||||
|
||||
hdr.cmd = DYNVC_CAPABILITIES
|
||||
hdr.cbChId = 0
|
||||
hdr.sp = 0
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
core.WriteUInt16LE(0x0050, b)
|
||||
core.WriteUInt16LE(ver, b)
|
||||
c.Send(b.Bytes())
|
||||
}
|
||||
451
grdp/plugin/rail/rail.go
Normal file
451
grdp/plugin/rail/rail.go
Normal file
@@ -0,0 +1,451 @@
|
||||
// rail.go
|
||||
package rail
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
|
||||
"ShotRDP/grdp/core"
|
||||
"ShotRDP/grdp/glog"
|
||||
"ShotRDP/grdp/plugin"
|
||||
)
|
||||
|
||||
const (
|
||||
ChannelName = plugin.RAIL_SVC_CHANNEL_NAME
|
||||
ChannelOption = plugin.CHANNEL_OPTION_INITIALIZED | plugin.CHANNEL_OPTION_ENCRYPT_RDP |
|
||||
plugin.CHANNEL_OPTION_COMPRESS_RDP | plugin.CHANNEL_OPTION_SHOW_PROTOCOL
|
||||
)
|
||||
|
||||
const (
|
||||
TS_RAIL_ORDER_EXEC = 0x0001
|
||||
TS_RAIL_ORDER_ACTIVATE = 0x0002
|
||||
TS_RAIL_ORDER_SYSPARAM = 0x0003
|
||||
TS_RAIL_ORDER_SYSCOMMAND = 0x0004
|
||||
TS_RAIL_ORDER_HANDSHAKE = 0x0005
|
||||
TS_RAIL_ORDER_NOTIFY_EVENT = 0x0006
|
||||
TS_RAIL_ORDER_WINDOWMOVE = 0x0008
|
||||
TS_RAIL_ORDER_LOCALMOVESIZE = 0x0009
|
||||
TS_RAIL_ORDER_MINMAXINFO = 0x000A
|
||||
TS_RAIL_ORDER_CLIENTSTATUS = 0x000B
|
||||
TS_RAIL_ORDER_SYSMENU = 0x000C
|
||||
TS_RAIL_ORDER_LANGBARINFO = 0x000D
|
||||
TS_RAIL_ORDER_GET_APPID_REQ = 0x000E
|
||||
TS_RAIL_ORDER_GET_APPID_RESP = 0x000F
|
||||
TS_RAIL_ORDER_TASKBARINFO = 0x0010
|
||||
TS_RAIL_ORDER_LANGUAGEIMEINFO = 0x0011
|
||||
TS_RAIL_ORDER_COMPARTMENTINFO = 0x0012
|
||||
TS_RAIL_ORDER_HANDSHAKE_EX = 0x0013
|
||||
TS_RAIL_ORDER_ZORDER_SYNC = 0x0014
|
||||
TS_RAIL_ORDER_CLOAK = 0x0015
|
||||
TS_RAIL_ORDER_POWER_DISPLAY_REQUEST = 0x0016
|
||||
TS_RAIL_ORDER_SNAP_ARRANGE = 0x0017
|
||||
TS_RAIL_ORDER_GET_APPID_RESP_EX = 0x0018
|
||||
TS_RAIL_ORDER_EXEC_RESULT = 0x0080
|
||||
)
|
||||
|
||||
type RailClient struct {
|
||||
w core.ChannelSender
|
||||
DesktopWidth uint16
|
||||
DesktopHeight uint16
|
||||
RemoteApplicationProgram string
|
||||
ShellWorkingDirectory string
|
||||
RemoteApplicationCmdLine string
|
||||
}
|
||||
|
||||
func NewClient() *RailClient {
|
||||
return &RailClient{
|
||||
DesktopWidth: 800,
|
||||
DesktopHeight: 600,
|
||||
RemoteApplicationProgram: "calc",
|
||||
ShellWorkingDirectory: "/tmp",
|
||||
}
|
||||
}
|
||||
|
||||
type RailPDUHeader struct {
|
||||
OrderType uint16 `struc:"little"`
|
||||
OrderLength uint16 `struc:"little"`
|
||||
}
|
||||
|
||||
func NewRailPDUHeader(mType, ln uint16) *RailPDUHeader {
|
||||
return &RailPDUHeader{
|
||||
OrderType: mType,
|
||||
OrderLength: ln,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *RailPDUHeader) serialize() []byte {
|
||||
b := &bytes.Buffer{}
|
||||
core.WriteUInt16LE(h.OrderType, b)
|
||||
core.WriteUInt16LE(h.OrderLength, b)
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
func (c *RailClient) sendData(mType uint16, ln int, s []byte) {
|
||||
glog.Debug(ln, ":ln:", len(s), "data:", hex.EncodeToString(s))
|
||||
header := NewRailPDUHeader(mType, uint16(ln))
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
core.WriteBytes(header.serialize(), b)
|
||||
core.WriteBytes(s, b)
|
||||
|
||||
c.Send(b.Bytes())
|
||||
}
|
||||
|
||||
func (c *RailClient) Send(s []byte) (int, error) {
|
||||
glog.Debug("len:", len(s), "data:", hex.EncodeToString(s))
|
||||
name, _ := c.GetType()
|
||||
return c.w.SendToChannel(name, s)
|
||||
}
|
||||
func (c *RailClient) Sender(f core.ChannelSender) {
|
||||
c.w = f
|
||||
}
|
||||
func (c *RailClient) GetType() (string, uint32) {
|
||||
return ChannelName, ChannelOption
|
||||
}
|
||||
|
||||
func (c *RailClient) Process(s []byte) {
|
||||
glog.Debug("recv:", hex.EncodeToString(s))
|
||||
r := bytes.NewReader(s)
|
||||
msgType, _ := core.ReadUint16LE(r)
|
||||
length, _ := core.ReadUint16LE(r)
|
||||
|
||||
glog.Infof("rail: type=0x%x length=%d, all=%d", msgType, length, r.Len())
|
||||
|
||||
b, _ := core.ReadBytes(int(length), r)
|
||||
glog.Info("b:", hex.EncodeToString(b))
|
||||
|
||||
switch msgType {
|
||||
case TS_RAIL_ORDER_HANDSHAKE:
|
||||
glog.Info("TS_RAIL_ORDER_HANDSHAKE")
|
||||
c.processOrderHandshake(b)
|
||||
case TS_RAIL_ORDER_SYSPARAM:
|
||||
glog.Info("TS_RAIL_ORDER_SYSPARAM")
|
||||
c.processOrderSysparam(b)
|
||||
case TS_RAIL_ORDER_EXEC_RESULT:
|
||||
glog.Info("TS_RAIL_ORDER_EXEC_RESULT")
|
||||
c.processExecResult(b)
|
||||
|
||||
default:
|
||||
glog.Errorf("type 0x%x not supported", msgType)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RailClient) processOrderHandshake(b []byte) {
|
||||
r := bytes.NewReader(b)
|
||||
buildNumber, _ := core.ReadUInt32LE(r)
|
||||
glog.Info("buildNumber:", buildNumber)
|
||||
|
||||
//send client info
|
||||
c.sendClientStatus()
|
||||
|
||||
//send client systemparam
|
||||
c.sendClientSystemparam()
|
||||
|
||||
//send client execute
|
||||
c.sendClientExecute()
|
||||
}
|
||||
|
||||
const (
|
||||
TS_RAIL_CLIENTSTATUS_ALLOWLOCALMOVESIZE = 0x00000001
|
||||
TS_RAIL_CLIENTSTATUS_AUTORECONNECT = 0x00000002
|
||||
TS_RAIL_CLIENTSTATUS_ZORDER_SYNC = 0x00000004
|
||||
TS_RAIL_CLIENTSTATUS_WINDOW_RESIZE_MARGIN_SUPPORTED = 0x00000010
|
||||
TS_RAIL_CLIENTSTATUS_HIGH_DPI_ICONS_SUPPORTED = 0x00000020
|
||||
TS_RAIL_CLIENTSTATUS_APPBAR_REMOTING_SUPPORTED = 0x00000040
|
||||
TS_RAIL_CLIENTSTATUS_POWER_DISPLAY_REQUEST_SUPPORTED = 0x00000080
|
||||
TS_RAIL_CLIENTSTATUS_GET_APPID_RESPONSE_EX_SUPPORTED = 0x00000100
|
||||
TS_RAIL_CLIENTSTATUS_BIDIRECTIONAL_CLOAK_SUPPORTED = 0x00000200
|
||||
)
|
||||
|
||||
func (c *RailClient) sendClientStatus() {
|
||||
glog.Info("Send client Status")
|
||||
var flags uint32 = TS_RAIL_CLIENTSTATUS_ALLOWLOCALMOVESIZE
|
||||
|
||||
//if (settings->AutoReconnectionEnabled)
|
||||
//clientStatus.flags |= TS_RAIL_CLIENTSTATUS_AUTORECONNECT;
|
||||
|
||||
flags |= TS_RAIL_CLIENTSTATUS_ZORDER_SYNC
|
||||
flags |= TS_RAIL_CLIENTSTATUS_WINDOW_RESIZE_MARGIN_SUPPORTED
|
||||
flags |= TS_RAIL_CLIENTSTATUS_APPBAR_REMOTING_SUPPORTED
|
||||
flags |= TS_RAIL_CLIENTSTATUS_POWER_DISPLAY_REQUEST_SUPPORTED
|
||||
flags |= TS_RAIL_CLIENTSTATUS_BIDIRECTIONAL_CLOAK_SUPPORTED
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
core.WriteUInt32LE(flags, b)
|
||||
|
||||
c.sendData(TS_RAIL_ORDER_CLIENTSTATUS, 4, b.Bytes())
|
||||
}
|
||||
|
||||
const (
|
||||
SPI_SET_SCREEN_SAVE_ACTIVE = 0x00000011
|
||||
SPI_SET_SCREEN_SAVE_SECURE = 0x00000077
|
||||
)
|
||||
const (
|
||||
/*Bit mask values for SPI_ parameters*/
|
||||
SPI_MASK_SET_DRAG_FULL_WINDOWS = 0x00000001
|
||||
SPI_MASK_SET_KEYBOARD_CUES = 0x00000002
|
||||
SPI_MASK_SET_KEYBOARD_PREF = 0x00000004
|
||||
SPI_MASK_SET_MOUSE_BUTTON_SWAP = 0x00000008
|
||||
SPI_MASK_SET_WORK_AREA = 0x00000010
|
||||
SPI_MASK_DISPLAY_CHANGE = 0x00000020
|
||||
SPI_MASK_TASKBAR_POS = 0x00000040
|
||||
SPI_MASK_SET_HIGH_CONTRAST = 0x00000080
|
||||
SPI_MASK_SET_SCREEN_SAVE_ACTIVE = 0x00000100
|
||||
SPI_MASK_SET_SET_SCREEN_SAVE_SECURE = 0x00000200
|
||||
SPI_MASK_SET_CARET_WIDTH = 0x00000400
|
||||
SPI_MASK_SET_STICKY_KEYS = 0x00000800
|
||||
SPI_MASK_SET_TOGGLE_KEYS = 0x00001000
|
||||
SPI_MASK_SET_FILTER_KEYS = 0x00002000
|
||||
)
|
||||
const (
|
||||
SPI_SET_DRAG_FULL_WINDOWS = 0x00000025
|
||||
SPI_SET_KEYBOARD_CUES = 0x0000100B
|
||||
SPI_SET_KEYBOARD_PREF = 0x00000045
|
||||
SPI_SET_MOUSE_BUTTON_SWAP = 0x00000021
|
||||
SPI_SET_WORK_AREA = 0x0000002F
|
||||
SPI_DISPLAY_CHANGE = 0x0000F001
|
||||
SPI_TASKBAR_POS = 0x0000F000
|
||||
SPI_SET_HIGH_CONTRAST = 0x00000043
|
||||
SPI_SETCARETWIDTH = 0x00002007
|
||||
SPI_SETSTICKYKEYS = 0x0000003B
|
||||
SPI_SETTOGGLEKEYS = 0x00000035
|
||||
SPI_SETFILTERKEYS = 0x00000033
|
||||
)
|
||||
|
||||
type TsFilterKeys struct {
|
||||
Flags uint32
|
||||
WaitTime uint32
|
||||
DelayTime uint32
|
||||
RepeatTime uint32
|
||||
BounceTime uint32
|
||||
}
|
||||
type RailHighContrast struct {
|
||||
flags uint32
|
||||
colorSchemeLength uint32
|
||||
colorScheme string
|
||||
}
|
||||
type Rectangle16 struct {
|
||||
left uint16
|
||||
top uint16
|
||||
right uint16
|
||||
bottom uint16
|
||||
}
|
||||
type RailSysparamOrder struct {
|
||||
param uint32
|
||||
params uint32
|
||||
dragFullWindows uint8
|
||||
keyboardCues uint8
|
||||
keyboardPref uint8
|
||||
mouseButtonSwap uint8
|
||||
workArea Rectangle16
|
||||
displayChange Rectangle16
|
||||
taskbarPos Rectangle16
|
||||
highContrast RailHighContrast
|
||||
caretWidth uint32
|
||||
stickyKeys uint32
|
||||
toggleKeys uint32
|
||||
filterKeys TsFilterKeys
|
||||
setScreenSaveActive uint8
|
||||
setScreenSaveSecure uint8
|
||||
}
|
||||
|
||||
func (c *RailClient) sendClientSystemparam() {
|
||||
glog.Info("Send client Systemparam")
|
||||
|
||||
var sp RailSysparamOrder
|
||||
sp.params = 0
|
||||
sp.params |= SPI_MASK_SET_HIGH_CONTRAST
|
||||
sp.highContrast.colorScheme = ""
|
||||
sp.highContrast.colorSchemeLength = 0
|
||||
sp.highContrast.flags = 0x7E
|
||||
sp.params |= SPI_MASK_SET_MOUSE_BUTTON_SWAP
|
||||
sp.mouseButtonSwap = 0
|
||||
sp.params |= SPI_MASK_SET_KEYBOARD_PREF
|
||||
sp.keyboardPref = 0
|
||||
sp.params |= SPI_MASK_SET_DRAG_FULL_WINDOWS
|
||||
sp.dragFullWindows = 0
|
||||
sp.params |= SPI_MASK_SET_KEYBOARD_CUES
|
||||
sp.keyboardCues = 0
|
||||
sp.params |= SPI_MASK_SET_WORK_AREA
|
||||
sp.workArea.left = 0
|
||||
sp.workArea.top = 0
|
||||
sp.workArea.right = c.DesktopWidth
|
||||
sp.workArea.bottom = c.DesktopHeight
|
||||
|
||||
if sp.params&SPI_MASK_SET_HIGH_CONTRAST != 0 {
|
||||
sp.param = SPI_SET_HIGH_CONTRAST
|
||||
c.sendOneClientSysparam(&sp)
|
||||
}
|
||||
|
||||
if sp.params&SPI_MASK_TASKBAR_POS != 0 {
|
||||
sp.param = SPI_TASKBAR_POS
|
||||
c.sendOneClientSysparam(&sp)
|
||||
}
|
||||
|
||||
if sp.params&SPI_MASK_SET_MOUSE_BUTTON_SWAP != 0 {
|
||||
sp.param = SPI_SET_MOUSE_BUTTON_SWAP
|
||||
c.sendOneClientSysparam(&sp)
|
||||
}
|
||||
|
||||
if sp.params&SPI_MASK_SET_KEYBOARD_PREF != 0 {
|
||||
sp.param = SPI_SET_KEYBOARD_PREF
|
||||
c.sendOneClientSysparam(&sp)
|
||||
}
|
||||
|
||||
if sp.params&SPI_MASK_SET_DRAG_FULL_WINDOWS != 0 {
|
||||
sp.param = SPI_SET_DRAG_FULL_WINDOWS
|
||||
c.sendOneClientSysparam(&sp)
|
||||
}
|
||||
|
||||
if sp.params&SPI_MASK_SET_KEYBOARD_CUES != 0 {
|
||||
sp.param = SPI_SET_KEYBOARD_CUES
|
||||
c.sendOneClientSysparam(&sp)
|
||||
}
|
||||
|
||||
if sp.params&SPI_MASK_SET_WORK_AREA != 0 {
|
||||
sp.param = SPI_SET_WORK_AREA
|
||||
glog.Debug("SPI_SET_WORK_AREA")
|
||||
c.sendOneClientSysparam(&sp)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RailClient) sendOneClientSysparam(sp *RailSysparamOrder) {
|
||||
length := 0
|
||||
b := &bytes.Buffer{}
|
||||
core.WriteUInt32LE(sp.param, b)
|
||||
switch sp.param {
|
||||
case SPI_SET_DRAG_FULL_WINDOWS:
|
||||
core.WriteUInt8(sp.dragFullWindows, b)
|
||||
|
||||
case SPI_SET_KEYBOARD_CUES:
|
||||
core.WriteUInt8(sp.keyboardCues, b)
|
||||
|
||||
case SPI_SET_KEYBOARD_PREF:
|
||||
core.WriteUInt8(sp.keyboardPref, b)
|
||||
|
||||
case SPI_SET_MOUSE_BUTTON_SWAP:
|
||||
core.WriteUInt8(sp.mouseButtonSwap, b)
|
||||
|
||||
case SPI_SET_WORK_AREA:
|
||||
core.WriteUInt16LE(sp.workArea.left, b)
|
||||
core.WriteUInt16LE(sp.workArea.top, b)
|
||||
core.WriteUInt16LE(sp.workArea.right, b)
|
||||
core.WriteUInt16LE(sp.workArea.bottom, b)
|
||||
|
||||
case SPI_DISPLAY_CHANGE:
|
||||
core.WriteUInt16LE(sp.displayChange.left, b)
|
||||
core.WriteUInt16LE(sp.displayChange.top, b)
|
||||
core.WriteUInt16LE(sp.displayChange.right, b)
|
||||
core.WriteUInt16LE(sp.displayChange.bottom, b)
|
||||
|
||||
case SPI_TASKBAR_POS:
|
||||
core.WriteUInt16LE(sp.taskbarPos.left, b)
|
||||
core.WriteUInt16LE(sp.taskbarPos.top, b)
|
||||
core.WriteUInt16LE(sp.taskbarPos.right, b)
|
||||
core.WriteUInt16LE(sp.taskbarPos.bottom, b)
|
||||
|
||||
case SPI_SET_HIGH_CONTRAST:
|
||||
core.WriteUInt32LE(sp.highContrast.flags, b)
|
||||
core.WriteUInt32LE(sp.highContrast.colorSchemeLength, b)
|
||||
data := core.UnicodeEncode(sp.highContrast.colorScheme)
|
||||
core.WriteBytes(data, b)
|
||||
|
||||
case SPI_SETFILTERKEYS:
|
||||
core.WriteUInt32LE(sp.filterKeys.Flags, b)
|
||||
core.WriteUInt32LE(sp.filterKeys.WaitTime, b)
|
||||
core.WriteUInt32LE(sp.filterKeys.DelayTime, b)
|
||||
core.WriteUInt32LE(sp.filterKeys.RepeatTime, b)
|
||||
core.WriteUInt32LE(sp.filterKeys.BounceTime, b)
|
||||
|
||||
case SPI_SETSTICKYKEYS:
|
||||
core.WriteUInt32LE(sp.stickyKeys, b)
|
||||
|
||||
case SPI_SETCARETWIDTH:
|
||||
core.WriteUInt32LE(sp.caretWidth, b)
|
||||
|
||||
case SPI_SETTOGGLEKEYS:
|
||||
core.WriteUInt32LE(sp.toggleKeys, b)
|
||||
|
||||
case SPI_MASK_SET_SET_SCREEN_SAVE_SECURE:
|
||||
core.WriteUInt8(sp.setScreenSaveSecure, b)
|
||||
|
||||
case SPI_MASK_SET_SCREEN_SAVE_ACTIVE:
|
||||
core.WriteUInt8(sp.setScreenSaveActive, b)
|
||||
|
||||
default:
|
||||
glog.Error("ERROR_BAD_ARGUMENTS")
|
||||
return
|
||||
}
|
||||
|
||||
c.sendData(TS_RAIL_ORDER_SYSPARAM, length+b.Len(), b.Bytes())
|
||||
}
|
||||
|
||||
type RailExecOrder struct {
|
||||
flags uint16
|
||||
RemoteApplicationProgram string
|
||||
RemoteApplicationWorkingDir string
|
||||
RemoteApplicationArguments string
|
||||
}
|
||||
|
||||
func (c *RailClient) sendClientExecute() {
|
||||
glog.Info("Send Client Execute")
|
||||
var exec RailExecOrder
|
||||
//exec.flags = TS_RAIL_EXEC_FLAG_EXPAND_ARGUMENTS
|
||||
exec.RemoteApplicationProgram = c.RemoteApplicationProgram
|
||||
exec.RemoteApplicationWorkingDir = c.ShellWorkingDirectory
|
||||
exec.RemoteApplicationArguments = c.RemoteApplicationCmdLine
|
||||
|
||||
program := core.UnicodeEncode(exec.RemoteApplicationProgram)
|
||||
workdir := core.UnicodeEncode(exec.RemoteApplicationWorkingDir)
|
||||
arguments := core.UnicodeEncode(exec.RemoteApplicationArguments)
|
||||
|
||||
length := 4
|
||||
b := &bytes.Buffer{}
|
||||
core.WriteUInt16LE(exec.flags, b)
|
||||
core.WriteUInt16LE(uint16(len(program)), b)
|
||||
core.WriteUInt16LE(uint16(len(workdir)), b)
|
||||
core.WriteUInt16LE(uint16(len(arguments)), b)
|
||||
core.WriteBytes(program, b)
|
||||
core.WriteBytes(workdir, b)
|
||||
core.WriteBytes(arguments, b)
|
||||
length += b.Len()
|
||||
|
||||
c.sendData(TS_RAIL_ORDER_EXEC, length, b.Bytes())
|
||||
|
||||
}
|
||||
|
||||
func (c *RailClient) processOrderSysparam(b []byte) {
|
||||
r := bytes.NewReader(b)
|
||||
systemParam, _ := core.ReadUInt32LE(r)
|
||||
body, _ := core.ReadUInt8(r)
|
||||
glog.Infof("systemParam:0x%x, body:%d", systemParam, body)
|
||||
}
|
||||
|
||||
const (
|
||||
//The Client Execute request was successful and the requested application or file has been launched.
|
||||
RAIL_EXEC_S_OK = 0x0000
|
||||
//The Client Execute request could not be satisfied because the server is not monitoring the current input desktop.
|
||||
RAIL_EXEC_E_HOOK_NOT_LOADED = 0x0001
|
||||
//The Execute request could not be satisfied because the request PDU was malformed.
|
||||
RAIL_EXEC_E_DECODE_FAILED = 0x0002
|
||||
//The Client Execute request could not be satisfied because the requested application was blocked by policy from being launched on the server.
|
||||
RAIL_EXEC_E_NOT_IN_ALLOWLIST = 0x0003
|
||||
//The Client Execute request could not be satisfied because the application or file path could not be found.
|
||||
RAIL_EXEC_E_FILE_NOT_FOUND = 0x0005
|
||||
//The Client Execute request could not be satisfied because an unspecified error occurred on the server.
|
||||
RAIL_EXEC_E_FAIL = 0x0006
|
||||
//The Client Execute request could not be satisfied because the remote session is locked.
|
||||
RAIL_EXEC_E_SESSION_LOCKED = 0x0007
|
||||
)
|
||||
|
||||
func (c *RailClient) processExecResult(b []byte) {
|
||||
r := bytes.NewReader(b)
|
||||
flags, _ := core.ReadUint16LE(r)
|
||||
execResult, _ := core.ReadUint16LE(r)
|
||||
rawResult, _ := core.ReadUInt32LE(r)
|
||||
core.ReadUint16LE(r)
|
||||
exeOrFileLength, _ := core.ReadUint16LE(r)
|
||||
exeOrFile, _ := core.ReadBytes(r.Len(), r)
|
||||
glog.Info("flags:", flags, "execResult:", execResult, "rawResult:", rawResult)
|
||||
glog.Info("length:", exeOrFileLength, "file:", core.UnicodeDecode(exeOrFile))
|
||||
}
|
||||
28
grdp/plugin/rdpgfx/rdpgfx.go
Normal file
28
grdp/plugin/rdpgfx/rdpgfx.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package rdpgfx
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
|
||||
"ShotRDP/grdp/core"
|
||||
"ShotRDP/grdp/glog"
|
||||
"ShotRDP/grdp/plugin"
|
||||
)
|
||||
|
||||
const (
|
||||
ChannelName = plugin.RDPGFX_DVC_CHANNEL_NAME
|
||||
)
|
||||
|
||||
type gfxClient struct {
|
||||
}
|
||||
|
||||
func (c *gfxClient) Send(s []byte) (int, error) {
|
||||
glog.Debug("len:", len(s), "data:", hex.EncodeToString(s))
|
||||
name, _ := c.GetType()
|
||||
return c.w.SendToChannel(name, s)
|
||||
}
|
||||
func (c *gfxClient) Sender(f core.ChannelSender) {
|
||||
c.w = f
|
||||
}
|
||||
func (c *gfxClient) GetType() (string, uint32) {
|
||||
return ChannelName, 0
|
||||
}
|
||||
Reference in New Issue
Block a user