基本适应win7,win10,win server 08,win server 12,win server 16的截图

This commit is contained in:
2025-01-03 23:00:47 +08:00
parent 909b89dfce
commit 84362607c2
77 changed files with 69638 additions and 1 deletions

328
grdp/plugin/addins.go Normal file
View 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
View 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)
}

View 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())
}

View 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()
}
}

View 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
}

View 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
View 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
View 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))
}

View 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
}