package rdp import ( "bytes" "context" "encoding/binary" "encoding/hex" "errors" "fmt" "io" "os" "time" "unicode/utf16" "ezops/pkg/rdp/credssp" "ezops/pkg/rdp/transport" ) const ( mcsConnectInitial = 0x65 mcsConnectResponse = 0x66 mcsErectDomainRequest = 1 mcsAttachUserRequest = 10 mcsAttachUserConfirm = 11 mcsChannelJoinRequest = 14 mcsChannelJoinConfirm = 15 mcsSendDataRequest = 25 mcsSendDataIndication = 26 mcsGlobalChannelID = 1003 mcsUserChannelIDOffset = 1001 gccCSCore = 0xC001 gccCSSecurity = 0xC002 gccCSNet = 0xC003 gccSCCore = 0x0C01 gccSCSecurity = 0x0C02 gccSCNet = 0x0C03 // 静态虚拟通道声明(MS-RDPBCGR 2.2.7.1.1 Channel Pool) // channel name 固定 8 字节 ASCII,不足补 0;options 控制通道加密/压缩行为 // Logan.Li channelNameRdpdr = "rdpdr" channelNameRdpsnd = "rdpsnd" channelNameCliprdr = "cliprdr" channelNameDrdynvc = "drdynvc" channelOptionInitialized = 0x80000000 channelOptionEncryptRDP = 0x40000000 channelOptionShowProtocol = 0x00200000 channelOptionCompressRDP = 0x00800000 secFlagInfoPkt = 0x0040 secFlagLicensePkt = 0x0080 licenseErrorAlert = 0xff licenseStatusValid = 0x00000007 licenseStateNoTransit = 0x00000002 pduTypeDemandActive = 0x0011 pduTypeConfirmActive = 0x0013 pduTypeDeactivateAll = 0x0016 pduTypeData = 0x0017 pduTypeServerRedirect = 0x001a pduType2Synchronize = 0x1f pduType2Control = 0x14 pduType2FontList = 0x27 pduType2FontMap = 0x28 pduType2RefreshRect = 0x21 pduType2SetErrorInfo = 0x2f pduType2SuppressOutput = 0x23 pduType2SaveSessionInfo = 0x26 pduType2FrameAcknowledge = 0x38 ctrlActionGrantedControl = 0x0002 ) var errFinalizeActive = errors.New("RDP 会话已进入图形更新流") // MCSProbeInfo 记录 NLA 后 MCS/GCC 连接期响应,用于确认图形会话层入口是否跑通 // Logan.Li type MCSProbeInfo struct { HandshakeInfo UserDataBytes int ServerCoreVersion uint32 ServerChannelID uint16 ServerChannelCount uint16 ServerChannelIDs []uint16 // ChannelNameByID 记录服务端在 Connect-Response 里分配的 channel ID 对应的名称。 // 客户端在 Connect-Initial 里声明的 channel def 顺序与服务端返回的 ServerChannelIDs 一一对应, // 据此建立 ID→名称映射,供 readLoop 按通道分发虚拟通道数据(cliprdr/rdpdr)。// Logan.Li ChannelNameByID map[uint16]string ServerEncryption uint32 ServerEncryptionLvl uint32 UserChannelID uint16 MessageChannelID uint16 JoinedChannels []uint16 FirstDataChannelID uint16 FirstSecurityFlags uint16 FirstPayloadBytes int LicenseMessageType uint8 LicenseErrorCode uint32 LicenseState uint32 DemandPDUType uint16 DemandSharedID uint32 DemandCapabilities uint16 DemandSource string PostConfirmTypes []uint8 } // ProbeMCSConnect 复用同一条 CredSSP TLS 连接发送 MCS Connect Initial 并解析 Connect Response // Logan.Li func ProbeMCSConnect(ctx context.Context, cfg Config) (MCSProbeInfo, error) { port := cfg.Port if port == 0 { port = 3389 } address := fmt.Sprintf("%s:%d", cfg.Host, port) start := time.Now() rdpDebugf(cfg, "probe start address=%s mode=%q", address, cfg.MCSMode) strategies := []credssp.Options{ {Version: 6, PubKeyStyle: "binding"}, {Version: 6, PubKeyStyle: "binding", UseCBT: true}, {Version: 6, PubKeyStyle: "binding", ForceMIC: true}, {Version: 2, PubKeyStyle: "raw"}, } var lastErr error for _, opts := range strategies { rdpDebugf(cfg, "strategy start version=%d pubkey=%s cbt=%v mic=%v", opts.Version, opts.PubKeyStyle, opts.UseCBT, opts.ForceMIC) info, err := probeMCSConnectOnce(ctx, cfg, address, opts) if err == nil { info.Duration = time.Since(start) rdpDebugf(cfg, "probe ok duration=%s joined=%v post=%v", info.Duration.Round(time.Millisecond), info.JoinedChannels, info.PostConfirmTypes) return info, nil } rdpDebugf(cfg, "strategy failed version=%d pubkey=%s err=%v", opts.Version, opts.PubKeyStyle, err) lastErr = err } if lastErr == nil { lastErr = fmt.Errorf("MCS Connect Initial 失败") } return MCSProbeInfo{HandshakeInfo: HandshakeInfo{Address: address, Duration: time.Since(start)}}, lastErr } func probeMCSConnectOnce(ctx context.Context, cfg Config, address string, opts credssp.Options) (MCSProbeInfo, error) { start := time.Now() tlsConn, result, err := dialCredSSPOnce(ctx, cfg, address, opts) if err != nil { return MCSProbeInfo{}, err } defer tlsConn.Close() rdpDebugf(cfg, "credssp ok protocol=%s elapsed=%s", result.ProtocolName(), time.Since(start).Round(time.Millisecond)) _ = tlsConn.SetDeadline(time.Now().Add(30 * time.Second)) rdpDebugf(cfg, "send MCS ConnectInitial") if err := transport.WriteX224Data(tlsConn, buildMCSConnectInitial(cfg)); err != nil { return MCSProbeInfo{}, err } payload, err := transport.ReadX224Data(tlsConn) if err != nil { return MCSProbeInfo{}, err } if cfg.Debug { debugGCCServerBlocks(cfg, payload) } mcsInfo, err := parseMCSConnectResponse(payload) if err != nil { return MCSProbeInfo{}, err } rdpDebugf(cfg, "connect response userData=%d channel=%d channels=%v enc=%d/%d", mcsInfo.UserDataBytes, mcsInfo.ServerChannelID, mcsInfo.ServerChannelIDs, mcsInfo.ServerEncryption, mcsInfo.ServerEncryptionLvl) userID, err := attachMCSUser(tlsConn) if err != nil { return MCSProbeInfo{}, err } mcsInfo.UserChannelID = userID rdpDebugf(cfg, "attach user ok userID=%d", userID) joinIDs := append([]uint16{mcsGlobalChannelID, userID}, mcsInfo.ServerChannelIDs...) if mcsInfo.MessageChannelID != 0 { joinIDs = append(joinIDs, mcsInfo.MessageChannelID) } for _, channelID := range joinIDs { joined, err := joinMCSChannel(tlsConn, userID, channelID) if err != nil { return MCSProbeInfo{}, err } mcsInfo.JoinedChannels = append(mcsInfo.JoinedChannels, joined) rdpDebugf(cfg, "join channel ok channel=%d", joined) } first, demand, err := sendClientInfoAndReadDemand(tlsConn, cfg, userID) if err != nil { return MCSProbeInfo{}, err } mcsInfo.FirstDataChannelID = first.channelID mcsInfo.FirstSecurityFlags = first.securityFlags mcsInfo.FirstPayloadBytes = len(first.payload) mcsInfo.LicenseMessageType = first.licenseMsgType mcsInfo.LicenseErrorCode = first.licenseErrorCode mcsInfo.LicenseState = first.licenseState mcsInfo.DemandPDUType = demand.pduType mcsInfo.DemandSharedID = demand.sharedID mcsInfo.DemandCapabilities = demand.capabilityCount mcsInfo.DemandSource = string(demand.sourceDescriptor) rdpDebugf(cfg, "demand active shared=0x%08x caps=%d source=%q firstSecurity=0x%04x firstPayload=%d", demand.sharedID, demand.capabilityCount, string(demand.sourceDescriptor), first.securityFlags, len(first.payload)) postTypes, _, err := sendConfirmActiveAndFinalize(tlsConn, cfg, userID, demand) if err != nil { return MCSProbeInfo{}, fmt.Errorf("Client Finalize 失败: shared=0x%08x caps=%d source=%q: %w", demand.sharedID, demand.capabilityCount, string(demand.sourceDescriptor), err) } mcsInfo.PostConfirmTypes = postTypes state := tlsConn.ConnectionState() certSubject := "" if len(state.PeerCertificates) > 0 { certSubject = state.PeerCertificates[0].Subject.String() } mcsInfo.HandshakeInfo = HandshakeInfo{ Address: address, SelectedProtocol: result.ProtocolName(), TLSCipher: transport.TLSCipherSuiteName(state.CipherSuite), CertSubject: certSubject, } return mcsInfo, nil } func debugGCCServerBlocks(cfg Config, mcsPayload []byte) { info, err := parseMCSConnectResponse(mcsPayload) if err != nil { rdpDebugf(cfg, "debug gcc parse failed err=%v", err) return } rdpDebugf(cfg, "debug gcc summary userData=%d core=0x%08x scNet=%d msg=%d", info.UserDataBytes, info.ServerCoreVersion, info.ServerChannelID, info.MessageChannelID) } type mcsDataIndication struct { channelID uint16 securityFlags uint16 payload []byte licenseMsgType uint8 licenseErrorCode uint32 licenseState uint32 } type demandActiveProbe struct { pduType uint16 sharedID uint32 capabilityCount uint16 sourceDescriptor []byte } func sendClientInfoAndReadDemand(rw io.ReadWriter, cfg Config, userID uint16) (mcsDataIndication, demandActiveProbe, error) { infoPDU := buildClientInfoPDU(cfg) rdpDebugf(cfg, "send ClientInfo userID=%d bytes=%d", userID, len(infoPDU)) if err := transport.WriteX224Data(rw, buildMCSSendDataRequest(userID, mcsGlobalChannelID, infoPDU)); err != nil { return mcsDataIndication{}, demandActiveProbe{}, err } payload, err := transport.ReadX224Data(rw) if err != nil { return mcsDataIndication{}, demandActiveProbe{}, err } first, err := parseMCSDataIndication(payload) if err != nil { return mcsDataIndication{}, demandActiveProbe{}, err } if first.securityFlags&secFlagLicensePkt != 0 { rdpDebugf(cfg, "license packet security=0x%04x payload=%d", first.securityFlags, len(first.payload)) if err := parseLicensePacket(&first); err != nil { return first, demandActiveProbe{}, err } payload, err = transport.ReadX224Data(rw) if err != nil { return first, demandActiveProbe{}, err } } next, err := parseMCSDataIndication(payload) if err != nil { return first, demandActiveProbe{}, err } demand, err := parseDemandActiveProbe(next.payload) if err != nil { return first, demandActiveProbe{}, err } return first, demand, nil } func sendConfirmActiveAndFinalize(rw io.ReadWriter, cfg Config, userID uint16, demand demandActiveProbe) ([]uint8, []mcsDataIndication, error) { rdpDebugf(cfg, "finalize start userID=%d shared=0x%08x", userID, demand.sharedID) pdus := []struct { name string data []byte }{ {"ConfirmActive", buildConfirmActivePDU(cfg, userID, demand)}, {"Synchronize", buildDataPDU(userID, demand.sharedID, pduType2Synchronize, buildSynchronizeData(userID))}, {"ControlCooperate", buildDataPDU(userID, demand.sharedID, pduType2Control, buildControlData(0x0004))}, {"ControlRequest", buildDataPDU(userID, demand.sharedID, pduType2Control, buildControlData(0x0001))}, {"FontList", buildDataPDU(userID, demand.sharedID, pduType2FontList, buildFontListData())}, } var types []uint8 for _, pdu := range pdus { rdpDebugf(cfg, "finalize send %s bytes=%d", pdu.name, len(pdu.data)) if err := transport.WriteX224Data(rw, buildMCSSendDataRequest(userID, mcsGlobalChannelID, pdu.data)); err != nil { return nil, nil, fmt.Errorf("发送 %s 失败: %w", pdu.name, err) } if isRDPStepMode(cfg) { if stepTypes, err := readFinalizeStep(rw, pdu.name, 500*time.Millisecond); err != nil { return append(types, stepTypes...), nil, err } else if len(stepTypes) > 0 { types = append(types, stepTypes...) } } } readTypes, pending, err := waitFinalizeResponses(rw, cfg, 5*time.Second) if err != nil { return types, pending, err } types = append(types, readTypes...) return types, pending, nil } func isRDPStepMode(cfg Config) bool { return cfg.MCSMode == "step" || cfg.MCSMode == "step-legacy" || cfg.MCSMode == "step-bpp32" || cfg.MCSMode == "step-minimal" || cfg.MCSMode == "step-plain-info" || cfg.MCSMode == "step-grdp-comment" || cfg.MCSMode == "step-grdp-modern" } func readFinalizeStep(rw io.ReadWriter, name string, timeout time.Duration) ([]uint8, error) { conn, ok := rw.(interface{ SetReadDeadline(time.Time) error }) if !ok { return nil, nil } _ = conn.SetReadDeadline(time.Now().Add(timeout)) defer conn.SetReadDeadline(time.Time{}) payload, err := transport.ReadX224Data(rw) if err != nil { return nil, nil } data, err := parseMCSDataIndication(payload) if err != nil { return nil, fmt.Errorf("%s 后响应解析失败: %w", name, err) } got, err := parseShareDataPDUInfo(data.payload) if err != nil { return nil, fmt.Errorf("%s 后 Share Data 解析失败: %w", name, err) } if got.type2 == pduType2SetErrorInfo { return []uint8{got.type2}, fmt.Errorf("%s 后服务端返回 ErrorInfo: code=0x%08x(%s) raw=%s", name, got.errorInfo, rdpErrorInfoName(got.errorInfo), got.rawPrefix) } return []uint8{got.type2}, nil } func waitFinalizeResponses(rw io.ReadWriter, cfg Config, timeout time.Duration) ([]uint8, []mcsDataIndication, error) { if conn, ok := rw.(interface{ SetReadDeadline(time.Time) error }); ok { _ = conn.SetReadDeadline(time.Now().Add(timeout)) defer conn.SetReadDeadline(time.Time{}) } expected := []finalizeExpectation{ {type2: pduType2Synchronize}, {type2: pduType2Control, action: 0x0004}, {type2: pduType2Control, action: ctrlActionGrantedControl}, {type2: pduType2FontMap}, } var types []uint8 var pending []mcsDataIndication for _, want := range expected { for { payload, err := readNextSlowPathPayload(rw, cfg, timeout) if err != nil { if errors.Is(err, errFinalizeActive) || len(types) > 0 { rdpDebugf(cfg, "finalize accept active session types=%v err=%v", types, err) return types, pending, nil } return types, pending, fmt.Errorf("读取 Client Finalize 响应失败: %w", err) } data, err := parseMCSDataIndication(payload) if err != nil { return types, pending, err } // 虚拟通道数据(cliprdr/rdpdr 等,channel ≠ 1003 global)在 finalize 阶段 // 先缓存,等 SessionEngine 创建对应 handler 后再分发。 // Logan.Li if data.channelID != mcsGlobalChannelID { rdpDebugf(cfg, "finalize skip virtual channel data channel=%d payload=%d", data.channelID, len(data.payload)) pending = append(pending, data) continue } got, err := parseShareDataPDUInfo(data.payload) if err != nil { return types, pending, err } types = append(types, got.type2) rdpDebugf(cfg, "finalize recv type2=0x%02x action=0x%04x raw=%s", got.type2, got.controlAction, got.rawPrefix) if got.type2 == pduType2SetErrorInfo { return types, pending, fmt.Errorf("服务端返回 ErrorInfo: code=0x%08x(%s) seen=%v raw=%s", got.errorInfo, rdpErrorInfoName(got.errorInfo), types, got.rawPrefix) } if got.type2 == pduType2SaveSessionInfo { continue } if got.type2 != want.type2 { continue } if want.type2 == pduType2Control && got.controlAction != want.action { continue } break } } return types, pending, nil } func readNextSlowPathPayload(r io.Reader, cfg Config, timeout time.Duration) ([]byte, error) { deadline := time.Now().Add(timeout) seenFastPath := false for { pkt, err := ReadWirePacket(r) if err != nil { if seenFastPath { return nil, errFinalizeActive } return nil, err } if !pkt.FastPath { return pkt.Payload, nil } seenFastPath = true rdpDebugf(cfg, "finalize skip fastpath sec=0x%02x payload=%d", pkt.SecFlag, len(pkt.Payload)) if time.Now().After(deadline) { return nil, errFinalizeActive } } } type finalizeExpectation struct { type2 uint8 action uint16 } func buildConfirmActivePDU(cfg Config, userID uint16, demand demandActiveProbe) []byte { source := demand.sourceDescriptor if len(source) == 0 { source = []byte("rdpy") } body := bytes.Buffer{} writeLE32(&body, demand.sharedID) writeLE16(&body, 0x03ea) writeLE16(&body, uint16(len(source))) caps, capCount := buildConfirmCapabilities(cfg) rdpDebugf(cfg, "build ConfirmActive sourceLen=%d caps=%d capsBytes=%d", len(source), capCount, len(caps)) debugCapabilityList(cfg, caps) writeLE16(&body, uint16(len(caps)+4)) body.Write(source) writeLE16(&body, capCount) writeLE16(&body, 0) body.Write(caps) pdu := buildShareControlPDU(userID, pduTypeConfirmActive, body.Bytes()) prefixLen := len(pdu) if prefixLen > 96 { prefixLen = 96 } rdpDebugf(cfg, "ConfirmActive pduLen=%d prefix=%s", len(pdu), hex.EncodeToString(pdu[:prefixLen])) return pdu } func rdpDebugf(cfg Config, format string, args ...interface{}) { if !cfg.Debug { return } _, _ = fmt.Fprintf(os.Stderr, "[rdp] "+format+"\n", args...) } func rdpErrorInfoName(code uint32) string { switch code { case 0x000010EA: return "ERRINFO_BAD_CAPABILITIES" case 0x000010E9: return "ERRINFO_NO_CURSOR_CACHE" case 0x000010E8: return "ERRINFO_CAPABILITY_SET_TOO_LARGE" case 0x000010E7: return "ERRINFO_CAPABILITY_SET_TOO_SMALL" case 0x000010D4: return "ERRINFO_CONFIRM_ACTIVE_HAS_WRONG_SHAREID" case 0x000010D5: return "ERRINFO_CONFIRM_ACTIVE_HAS_WRONG_ORIGINATOR" default: return "UNKNOWN" } } func debugCapabilityList(cfg Config, caps []byte) { if !cfg.Debug { return } offset := 0 index := 0 for offset+4 <= len(caps) { capType := binary.LittleEndian.Uint16(caps[offset : offset+2]) capLen := int(binary.LittleEndian.Uint16(caps[offset+2 : offset+4])) if capLen < 4 || offset+capLen > len(caps) { rdpDebugf(cfg, "cap[%02d] invalid offset=%d type=0x%04x len=%d total=%d", index, offset, capType, capLen, len(caps)) return } body := caps[offset+4 : offset+capLen] prefixLen := len(body) if prefixLen > 24 { prefixLen = 24 } rdpDebugf(cfg, "cap[%02d] type=0x%04x len=%d body=%s", index, capType, capLen, hex.EncodeToString(body[:prefixLen])) offset += capLen index++ } if offset != len(caps) { rdpDebugf(cfg, "cap trailing bytes=%d offset=%d total=%d", len(caps)-offset, offset, len(caps)) } } func buildConfirmCapabilities(cfg Config) ([]byte, uint16) { width := cfg.Width if width <= 0 { width = 1280 } height := cfg.Height if height <= 0 { height = 800 } out := bytes.Buffer{} count := uint16(0) addCapability := func(capType uint16, body []byte) { writeCapability(&out, capType, body) count++ } if cfg.MCSMode == "comment" || cfg.MCSMode == "step-comment" || cfg.MCSMode == "grdp-comment" || cfg.MCSMode == "step-grdp-comment" { return buildGRDPCommentCapabilities(cfg, width, height) } addCapability(0x0001, buildGeneralCapability()) bitmap := bytes.Buffer{} preferredBPP := uint16(24) if cfg.MCSMode == "bpp32" || cfg.MCSMode == "step-bpp32" || cfg.MCSMode == "modern" || cfg.MCSMode == "step-modern" || cfg.MCSMode == "raw" || cfg.MCSMode == "step-raw" || cfg.MCSMode == "grdp-modern" || cfg.MCSMode == "step-grdp-modern" || cfg.MCSMode == "grdp-raw" || cfg.MCSMode == "step-grdp-raw" { preferredBPP = 32 } writeLE16(&bitmap, preferredBPP) writeLE16(&bitmap, 1) writeLE16(&bitmap, 1) writeLE16(&bitmap, 1) writeLE16(&bitmap, uint16(width)) writeLE16(&bitmap, uint16(height)) writeLE16(&bitmap, 0) writeLE16(&bitmap, 1) if cfg.MCSMode == "raw" || cfg.MCSMode == "step-raw" || cfg.MCSMode == "grdp-raw" || cfg.MCSMode == "step-grdp-raw" { writeLE16(&bitmap, 0) } else { writeLE16(&bitmap, 1) } writeLE16(&bitmap, 0) writeLE16(&bitmap, 1) writeLE16(&bitmap, 0) addCapability(0x0002, bitmap.Bytes()) if cfg.MCSMode == "basic" { addCapability(0x000d, buildInputCapability()) return out.Bytes(), count } if cfg.MCSMode == "legacy" || cfg.MCSMode == "step-legacy" { addCapability(0x0003, mustHex("00000000000000000000000000000000010014000000010000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008403000000000000000000")) addCapability(0x0005, mustHex("0000000002000200")) addCapability(0x0007, mustHex("0000000000000000")) addCapability(0x0008, buildPointerCapability(128)) addCapability(0x0009, mustHex("00000000")) addCapability(0x000a, mustHex("06000000")) addCapability(0x000c, mustHex("01000000")) addCapability(0x000d, buildInputCapability()) addCapability(0x000e, mustHex("01000000")) addCapability(0x000f, mustHex("01000000")) addCapability(0x0010, make([]byte, 48)) addCapability(0x0014, mustHex("0000000040060000")) return out.Bytes(), count } if cfg.MCSMode == "minimal" || cfg.MCSMode == "step-minimal" { addCapability(0x0003, mustHex("00000000000000000000000000000000010014000000010000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008403000000000000000000")) addCapability(0x0008, mustHex("00001400")) addCapability(0x000d, buildInputCapability()) addCapability(0x000e, mustHex("01000000")) return out.Bytes(), count } if cfg.MCSMode == "modern" || cfg.MCSMode == "step-modern" || cfg.MCSMode == "raw" || cfg.MCSMode == "step-raw" || cfg.MCSMode == "grdp-modern" || cfg.MCSMode == "step-grdp-modern" || cfg.MCSMode == "grdp-raw" || cfg.MCSMode == "step-grdp-raw" { addCapability(0x0003, buildGRDPOrderCapability()) addCapability(0x0005, mustHex("0000000002000200")) addCapability(0x0007, mustHex("0000000000000000")) addCapability(0x0008, buildPointerCapability(20)) addCapability(0x0009, mustHex("00000000")) addCapability(0x000a, mustHex("06000000")) addCapability(0x000c, mustHex("01000000")) addCapability(0x000d, buildInputCapability()) addCapability(0x000e, mustHex("01000000")) addCapability(0x000f, mustHex("01000000")) addCapability(0x0010, buildGlyphNoneCapability()) addCapability(0x001d, buildBitmapCodecsCapability()) addCapability(0x0013, buildBitmapCacheRev2CapabilityWithPersist(2)) addCapability(0x0014, mustHex("0000000040060000")) addCapability(0x001a, mustHex("00003f00")) addCapability(0x0017, mustHex("ff000000")) addCapability(0x001b, mustHex("0100")) addCapability(0x0019, mustHex("0100")) addCapability(0x001c, mustHex("5200000000000000")) addCapability(0x001e, mustHex("02000000")) return out.Bytes(), count } addCapability(0x0003, mustHex("00000000000000000000000000000000010014000000010000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008403000000000000000000")) addCapability(0x0008, buildPointerCapability(128)) addCapability(0x000c, mustHex("00000000")) addCapability(0x000d, buildInputCapability()) addCapability(0x000f, mustHex("00000000")) addCapability(0x0010, make([]byte, 48)) addCapability(0x0013, buildBitmapCacheRev2Capability()) addCapability(0x0011, mustHex("0000000000000000")) addCapability(0x0014, mustHex("0000000040060000")) addCapability(0x001a, mustHex("00000000")) return out.Bytes(), count } func buildGRDPCommentCapabilities(cfg Config, width int, height int) ([]byte, uint16) { out := bytes.Buffer{} count := uint16(0) addCapability := func(capType uint16, body []byte) { writeCapability(&out, capType, body) count++ } addCapability(0x0001, buildGeneralCapability()) bitmap := bytes.Buffer{} writeLE16(&bitmap, 24) writeLE16(&bitmap, 1) writeLE16(&bitmap, 1) writeLE16(&bitmap, 1) writeLE16(&bitmap, uint16(width)) writeLE16(&bitmap, uint16(height)) writeLE16(&bitmap, 0) writeLE16(&bitmap, 1) writeLE16(&bitmap, 1) writeLE16(&bitmap, 0) writeLE16(&bitmap, 1) writeLE16(&bitmap, 0) addCapability(0x0002, bitmap.Bytes()) addCapability(0x0003, mustHex("000000000000000000000000000000000000000000000000010014000000010000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000008403000000000000000000")) addCapability(0x0004, make([]byte, 36)) addCapability(0x0008, mustHex("00001400")) addCapability(0x000c, mustHex("00000000")) addCapability(0x000d, buildInputCapability()) addCapability(0x000f, mustHex("00000000")) addCapability(0x0010, buildGlyphNoneCapability()) addCapability(0x0011, mustHex("0000000000000000")) addCapability(0x0014, mustHex("0000000000000000")) addCapability(0x001a, mustHex("00000000")) rdpDebugf(cfg, "grdp-comment caps=%d bytes=%d", count, out.Len()) return out.Bytes(), count } func buildGRDPOrderCapability() []byte { out := bytes.Buffer{} out.Write(make([]byte, 16)) writeLE32(&out, 0) writeLE16(&out, 1) writeLE16(&out, 20) writeLE16(&out, 0) writeLE16(&out, 1) writeLE16(&out, 0) writeLE16(&out, 0x00aa) orderSupport := make([]byte, 32) // native 暂不处理 Orders,能力集必须全关,避免服务端只发订单导致桌面黑屏 // Logan.Li out.Write(orderSupport) writeLE16(&out, 0) writeLE16(&out, 0x0004) writeLE32(&out, 0) writeLE32(&out, 480*480) writeLE16(&out, 0) writeLE16(&out, 0) writeLE16(&out, 0x04e4) writeLE16(&out, 0) return out.Bytes() } func buildGeneralCapability() []byte { out := bytes.Buffer{} writeLE16(&out, 0x0001) writeLE16(&out, 0x0003) writeLE16(&out, 0x0200) writeLE16(&out, 0) writeLE16(&out, 0x0002) writeLE16(&out, 0x041d) writeLE16(&out, 0) writeLE16(&out, 0) writeLE16(&out, 0) out.WriteByte(1) out.WriteByte(1) return out.Bytes() } func buildPointerCapability(cacheSize uint16) []byte { out := bytes.Buffer{} writeLE16(&out, 1) writeLE16(&out, cacheSize) writeLE16(&out, cacheSize) return out.Bytes() } func buildBitmapCacheRev2Capability() []byte { return buildBitmapCacheRev2CapabilityWithPersist(0) } func buildBitmapCacheRev2CapabilityWithPersist(persist uint16) []byte { out := bytes.Buffer{} writeLE16(&out, persist) out.WriteByte(0) out.WriteByte(5) for _, cells := range []uint32{0x258, 0x258, 0x800, 0x1000, 0x800} { writeLE32(&out, cells) } out.Write(make([]byte, 12)) return out.Bytes() } func buildGlyphNoneCapability() []byte { out := bytes.Buffer{} out.Write(make([]byte, 40)) writeLE32(&out, 0) writeLE16(&out, 0) writeLE16(&out, 0) return out.Bytes() } func buildBitmapCodecsCapability() []byte { out := bytes.Buffer{} out.WriteByte(2) out.Write(mustHex("b91b8dca0f004f15589fae2d1a87e2d6")) out.WriteByte(1) writeLE16(&out, 3) out.Write([]byte{1, 1, 3}) out.Write(mustHex("122f777672bd6344afb3b73c9c6f7886")) out.WriteByte(3) rfx := buildRemoteFXProperties() writeLE16(&out, uint16(len(rfx))) out.Write(rfx) return out.Bytes() } func buildRemoteFXProperties() []byte { return mustHex("29000000000000001d000000c0cb080000000100c1cb1500000001c0cf010008000001400001010104") } func buildInputCapability() []byte { out := bytes.Buffer{} writeLE16(&out, 0x003d) writeLE16(&out, 0) writeLE32(&out, 0x00000409) writeLE32(&out, 4) writeLE32(&out, 0) writeLE32(&out, 12) out.Write(make([]byte, 64)) return out.Bytes() } func writeCapability(w io.Writer, capType uint16, body []byte) { writeLE16(w, capType) writeLE16(w, uint16(len(body)+4)) _, _ = w.Write(body) } func buildShareControlPDU(userID uint16, pduType uint16, body []byte) []byte { out := bytes.Buffer{} writeLE16(&out, uint16(len(body)+6)) writeLE16(&out, pduType) writeLE16(&out, userID) out.Write(body) return out.Bytes() } func buildDataPDU(userID uint16, sharedID uint32, type2 uint8, body []byte) []byte { data := bytes.Buffer{} writeLE32(&data, sharedID) data.WriteByte(0) data.WriteByte(1) writeLE16(&data, uint16(len(body)+4)) data.WriteByte(type2) data.WriteByte(0) writeLE16(&data, 0) data.Write(body) return buildShareControlPDU(userID, pduTypeData, data.Bytes()) } func buildSynchronizeData(userID uint16) []byte { out := bytes.Buffer{} writeLE16(&out, 1) writeLE16(&out, userID) return out.Bytes() } func buildControlData(action uint16) []byte { out := bytes.Buffer{} writeLE16(&out, action) writeLE16(&out, 0) writeLE32(&out, 0) return out.Bytes() } func buildFontListData() []byte { out := bytes.Buffer{} writeLE16(&out, 0) writeLE16(&out, 0) writeLE16(&out, 0x0003) writeLE16(&out, 0x0032) return out.Bytes() } func attachMCSUser(rw io.ReadWriter) (uint16, error) { if err := transport.WriteX224Data(rw, buildMCSErectDomainRequest()); err != nil { return 0, err } if err := transport.WriteX224Data(rw, buildMCSAttachUserRequest()); err != nil { return 0, err } payload, err := transport.ReadX224Data(rw) if err != nil { return 0, err } return parseMCSAttachUserConfirm(payload) } func joinMCSChannel(rw io.ReadWriter, userID uint16, channelID uint16) (uint16, error) { if err := transport.WriteX224Data(rw, buildMCSChannelJoinRequest(userID, channelID)); err != nil { return 0, err } payload, err := transport.ReadX224Data(rw) if err != nil { return 0, err } return parseMCSChannelJoinConfirm(payload, userID, channelID) } func buildMCSErectDomainRequest() []byte { out := bytes.Buffer{} writeMCSPDUHeader(&out, mcsErectDomainRequest) writePERInteger(&out, 0) writePERInteger(&out, 0) return out.Bytes() } func buildMCSAttachUserRequest() []byte { out := bytes.Buffer{} writeMCSPDUHeader(&out, mcsAttachUserRequest) return out.Bytes() } func buildMCSChannelJoinRequest(userID uint16, channelID uint16) []byte { out := bytes.Buffer{} writeMCSPDUHeader(&out, mcsChannelJoinRequest) writeBE16Value(&out, userID-mcsUserChannelIDOffset) writeBE16Value(&out, channelID) return out.Bytes() } func buildMCSSendDataRequest(userID uint16, channelID uint16, data []byte) []byte { out := bytes.Buffer{} writeMCSPDUHeader(&out, mcsSendDataRequest) writeBE16Value(&out, userID-mcsUserChannelIDOffset) writeBE16Value(&out, channelID) out.WriteByte(0x70) writePERLength(&out, len(data)) out.Write(data) return out.Bytes() } func parseMCSAttachUserConfirm(data []byte) (uint16, error) { r := bytes.NewReader(data) pdu, err := readMCSPDUHeader(r) if err != nil { return 0, err } if pdu != mcsAttachUserConfirm { return 0, fmt.Errorf("MCS AttachUserConfirm 类型错误: %d", pdu) } result, err := r.ReadByte() if err != nil { return 0, err } if result != 0 { return 0, fmt.Errorf("MCS AttachUser 被拒绝: %d", result) } userID, err := readBE16Value(r) if err != nil { return 0, err } return userID + mcsUserChannelIDOffset, nil } func parseMCSChannelJoinConfirm(data []byte, expectedUserID uint16, expectedChannelID uint16) (uint16, error) { r := bytes.NewReader(data) pdu, err := readMCSPDUHeader(r) if err != nil { return 0, err } if pdu != mcsChannelJoinConfirm { return 0, fmt.Errorf("MCS ChannelJoinConfirm 类型错误: %d", pdu) } result, err := r.ReadByte() if err != nil { return 0, err } userID, err := readBE16Value(r) if err != nil { return 0, err } userID += mcsUserChannelIDOffset channelID, err := readBE16Value(r) if err != nil { return 0, err } if userID != expectedUserID { return 0, fmt.Errorf("MCS ChannelJoin 用户不匹配: got=%d want=%d", userID, expectedUserID) } if channelID != expectedChannelID { return 0, fmt.Errorf("MCS ChannelJoin 通道不匹配: got=%d want=%d", channelID, expectedChannelID) } if result != 0 && (channelID == mcsGlobalChannelID || channelID == expectedUserID) { return 0, fmt.Errorf("MCS 必需通道加入失败: channel=%d result=%d", channelID, result) } return channelID, nil } // ParseMCSChannel 解析 MCS SendDataIndication,返回 channel ID(探针诊断用)。// Logan.Li func ParseMCSChannel(data []byte) (uint16, error) { ind, err := parseMCSDataIndication(data) if err != nil { return 0, err } return ind.channelID, nil } func parseMCSDataIndication(data []byte) (mcsDataIndication, error) { r := bytes.NewReader(data) pdu, err := readMCSPDUHeader(r) if err != nil { return mcsDataIndication{}, err } if pdu != mcsSendDataIndication { return mcsDataIndication{}, fmt.Errorf("MCS DataIndication 类型错误: %d", pdu) } if _, err := readBE16Value(r); err != nil { return mcsDataIndication{}, err } channelID, err := readBE16Value(r) if err != nil { return mcsDataIndication{}, err } if _, err := r.ReadByte(); err != nil { return mcsDataIndication{}, err } size, err := readPERLength(r) if err != nil { return mcsDataIndication{}, err } if int(size) > r.Len() { return mcsDataIndication{}, io.ErrUnexpectedEOF } payload := make([]byte, int(size)) _, _ = io.ReadFull(r, payload) flags := uint16(0) if len(payload) >= 4 { flags = binary.LittleEndian.Uint16(payload[0:2]) } return mcsDataIndication{channelID: channelID, securityFlags: flags, payload: payload}, nil } func parseLicensePacket(data *mcsDataIndication) error { if len(data.payload) < 8 { return fmt.Errorf("License PDU 太短: %d", len(data.payload)) } body := data.payload[4:] data.licenseMsgType = body[0] if len(body) >= 12 && data.licenseMsgType == licenseErrorAlert { data.licenseErrorCode = binary.LittleEndian.Uint32(body[4:8]) data.licenseState = binary.LittleEndian.Uint32(body[8:12]) if data.licenseErrorCode == licenseStatusValid && data.licenseState == licenseStateNoTransit { return nil } return fmt.Errorf("License ErrorAlert: code=0x%08x state=0x%08x", data.licenseErrorCode, data.licenseState) } return fmt.Errorf("暂未实现 License msgType=0x%02x 的响应", data.licenseMsgType) } func parseDemandActiveProbe(payload []byte) (demandActiveProbe, error) { r := bytes.NewReader(payload) totalLength, err := readLE16(r) if err != nil { return demandActiveProbe{}, err } pduType, err := readLE16(r) if err != nil { return demandActiveProbe{}, err } if int(totalLength) > len(payload) { return demandActiveProbe{}, fmt.Errorf("Share Control 长度超出数据: total=%d payload=%d", totalLength, len(payload)) } if pduType != pduTypeDemandActive { return demandActiveProbe{pduType: pduType}, fmt.Errorf("等待 DemandActive,收到 PDU type=0x%04x", pduType) } if _, err := readLE16(r); err != nil { return demandActiveProbe{}, err } sharedID, err := readLE32(r) if err != nil { return demandActiveProbe{}, err } sourceLen, err := readLE16(r) if err != nil { return demandActiveProbe{}, err } if _, err := readLE16(r); err != nil { return demandActiveProbe{}, err } if int(sourceLen) > r.Len() { return demandActiveProbe{}, io.ErrUnexpectedEOF } source := make([]byte, int(sourceLen)) _, _ = io.ReadFull(r, source) capabilityCount, err := readLE16(r) if err != nil { return demandActiveProbe{}, err } return demandActiveProbe{pduType: pduType, sharedID: sharedID, capabilityCount: capabilityCount, sourceDescriptor: source}, nil } func parseShareDataPDUType(payload []byte) (uint8, error) { info, err := parseShareDataPDUInfo(payload) if err != nil { return 0, err } return info.type2, nil } type shareDataPDUInfo struct { type2 uint8 controlAction uint16 errorInfo uint32 rawPrefix string } func parseShareDataPDUInfo(payload []byte) (shareDataPDUInfo, error) { if len(payload) < 18 { return shareDataPDUInfo{}, fmt.Errorf("Share Data PDU 太短: %d", len(payload)) } pduType := binary.LittleEndian.Uint16(payload[2:4]) if pduType != pduTypeData { return shareDataPDUInfo{}, fmt.Errorf("等待 Data PDU,收到 type=0x%04x", pduType) } prefixLen := len(payload) if prefixLen > 32 { prefixLen = 32 } info := shareDataPDUInfo{type2: payload[14], rawPrefix: hex.EncodeToString(payload[:prefixLen])} if info.type2 == pduType2Control { if len(payload) < 20 { return shareDataPDUInfo{}, fmt.Errorf("Control Data PDU 太短: %d", len(payload)) } info.controlAction = binary.LittleEndian.Uint16(payload[18:20]) } if info.type2 == pduType2SetErrorInfo { if len(payload) < 22 { return shareDataPDUInfo{}, fmt.Errorf("ErrorInfo Data PDU 太短: %d", len(payload)) } info.errorInfo = binary.LittleEndian.Uint32(payload[18:22]) } return info, nil } func mustHex(value string) []byte { data, err := hex.DecodeString(value) if err != nil { panic(err) } return data } func buildClientInfoPDU(cfg Config) []byte { info := buildRDPInfo(cfg) out := bytes.Buffer{} writeLE16(&out, secFlagInfoPkt) writeLE16(&out, 0) out.Write(info) return out.Bytes() } func buildRDPInfo(cfg Config) []byte { domain := utf16LENull(cfg.Domain) user := utf16LENull(cfg.Username) password := utf16LENull(cfg.Password) alternateShell := []byte{0, 0} workingDir := []byte{0, 0} flags := uint32(0x00000001 | 0x00000002 | 0x00000008 | 0x00000010 | 0x00000020 | 0x00000040 | 0x00000100 | 0x00004000 | 0x00010000 | 0x00020000) if cfg.MCSMode == "plain-info" || cfg.MCSMode == "step-plain-info" { flags = 0x00000001 | 0x00000002 | 0x00000008 | 0x00004000 } out := bytes.Buffer{} writeLE32(&out, 0) writeLE32(&out, flags) writeLE16(&out, uint16(len(domain)-2)) writeLE16(&out, uint16(len(user)-2)) writeLE16(&out, uint16(len(password)-2)) writeLE16(&out, uint16(len(alternateShell)-2)) writeLE16(&out, uint16(len(workingDir)-2)) out.Write(domain) out.Write(user) out.Write(password) out.Write(alternateShell) out.Write(workingDir) out.Write(buildExtendedInfo()) return out.Bytes() } func buildExtendedInfo() []byte { clientAddress := []byte{0, 0} clientDir := []byte{0, 0} out := bytes.Buffer{} writeLE16(&out, 0x0002) writeLE16(&out, uint16(len(clientAddress))) out.Write(clientAddress) writeLE16(&out, uint16(len(clientDir))) out.Write(clientDir) out.Write(make([]byte, 172)) writeLE32(&out, 0) writeLE32(&out, 0x00000001|0x00000002|0x00000004|0x00000080|0x00000100) return out.Bytes() } func buildMCSConnectInitial(cfg Config) []byte { userData := bytes.Buffer{} userData.Write(buildGCCClientCore(cfg)) userData.Write(buildGCCClientNetwork()) userData.Write(buildGCCClientSecurity()) userData.Write(buildGCCClientMessageChannel()) conference := buildGCCConferenceCreate(userData.Bytes()) body := bytes.Buffer{} writeBEROctetString(bodyWriter(&body), []byte{0x01}) writeBEROctetString(bodyWriter(&body), []byte{0x01}) writeBERBoolean(bodyWriter(&body), true) writeBERDomainParameters(bodyWriter(&body), 34, 2, 0, 0xffff) writeBERDomainParameters(bodyWriter(&body), 1, 1, 1, 0x0420) writeBERDomainParameters(bodyWriter(&body), 0xffff, 0xfc17, 0xffff, 0xffff) writeBEROctetString(bodyWriter(&body), conference) out := bytes.Buffer{} writeBERApplicationTag(bodyWriter(&out), mcsConnectInitial, body.Len()) out.Write(body.Bytes()) return out.Bytes() } func buildGCCClientCore(cfg Config) []byte { width := cfg.Width if width <= 0 { width = 1280 } height := cfg.Height if height <= 0 { height = 800 } highColorDepth := uint16(24) supportedColorDepths := uint16(0x000f) if cfg.MCSMode == "bpp32" || cfg.MCSMode == "step-bpp32" || cfg.MCSMode == "modern" || cfg.MCSMode == "step-modern" || cfg.MCSMode == "grdp-modern" || cfg.MCSMode == "step-grdp-modern" { highColorDepth = 32 supportedColorDepths = 0x001f } body := bytes.Buffer{} writeLE32(&body, 0x00080007) writeLE16(&body, uint16(width)) writeLE16(&body, uint16(height)) writeLE16(&body, 0xCA01) writeLE16(&body, 0xAA03) writeLE32(&body, 0x00000409) writeLE32(&body, 22621) body.Write(fixedUTF16LE("AIOPS", 32)) writeLE32(&body, 4) writeLE32(&body, 0) writeLE32(&body, 12) body.Write(make([]byte, 64)) writeLE16(&body, 0xCA01) writeLE16(&body, 1) writeLE32(&body, 0) writeLE16(&body, highColorDepth) writeLE16(&body, supportedColorDepths) writeLE16(&body, 0x00a3) body.Write(make([]byte, 64)) body.WriteByte(0x06) body.WriteByte(0) writeLE32(&body, transport.ProtocolCredSSP) return withGCCBlock(gccCSCore, body.Bytes()) } // declaredStaticChannels 是客户端在 MCS Connect-Initial 里声明的静态虚拟通道。 // 顺序与服务端 Connect-Response 的 SCNet.ChannelIdArray 一一对应,保持接近 MSTSC/FreeRDP 的通道组合。// Logan.Li var declaredStaticChannels = []struct { Name string Options uint32 }{ {Name: channelNameRdpdr, Options: channelOptionInitialized | channelOptionEncryptRDP}, {Name: channelNameRdpsnd, Options: channelOptionInitialized | channelOptionEncryptRDP}, {Name: channelNameCliprdr, Options: channelOptionInitialized | channelOptionEncryptRDP | channelOptionShowProtocol}, {Name: channelNameDrdynvc, Options: channelOptionInitialized | channelOptionCompressRDP}, } func buildGCCClientNetwork() []byte { body := bytes.Buffer{} writeLE32(&body, uint32(len(declaredStaticChannels))) // channelCount for _, ch := range declaredStaticChannels { // channel name: 8 字节 ASCII,不足补 0 // Logan.Li nameBuf := make([]byte, 8) copy(nameBuf, ch.Name) body.Write(nameBuf) writeLE32(&body, ch.Options) } return withGCCBlock(gccCSNet, body.Bytes()) } func buildGCCClientSecurity() []byte { body := bytes.Buffer{} writeLE32(&body, 0x0000000b) writeLE32(&body, 0) return withGCCBlock(gccCSSecurity, body.Bytes()) } func buildGCCClientMessageChannel() []byte { body := bytes.Buffer{} writeLE32(&body, 0) return withGCCBlock(0xC006, body.Bytes()) } func withGCCBlock(blockType uint16, body []byte) []byte { out := bytes.Buffer{} writeLE16(&out, blockType) writeLE16(&out, uint16(len(body)+4)) out.Write(body) return out.Bytes() } func buildGCCConferenceCreate(userData []byte) []byte { out := bytes.Buffer{} writePERChoice(&out, 0) writePERObjectIdentifier(&out, []byte{0, 0, 20, 124, 0, 1}) writePERLength(&out, len(userData)+14) writePERChoice(&out, 0) out.WriteByte(0x08) writePERNumericString(&out, "1", 1) out.WriteByte(0) out.WriteByte(1) writePERChoice(&out, 0xc0) writePEROctetStream(&out, []byte("Duca"), 4) writePEROctetStream(&out, userData, 0) return out.Bytes() } func parseMCSConnectResponse(data []byte) (MCSProbeInfo, error) { r := bytes.NewReader(data) if err := readBERApplicationTag(r, mcsConnectResponse); err != nil { return MCSProbeInfo{}, err } if _, err := readBEREnumerated(r); err != nil { return MCSProbeInfo{}, err } if _, err := readBERInteger(r); err != nil { return MCSProbeInfo{}, err } if err := skipBERDomainParameters(r); err != nil { return MCSProbeInfo{}, err } userData, err := readBEROctetString(r) if err != nil { return MCSProbeInfo{}, err } info := MCSProbeInfo{UserDataBytes: len(userData)} parseGCCConferenceResponse(userData, &info) return info, nil } func parseGCCConferenceResponse(data []byte, info *MCSProbeInfo) { r := bytes.NewReader(data) _, _ = r.ReadByte() _ = readPERObjectIdentifier(r, []byte{0, 0, 20, 124, 0, 1}) _, _ = readPERLength(r) _, _ = r.ReadByte() _, _ = readBE16(r) _, _ = readPERInteger(r) _, _ = r.ReadByte() _, _ = r.ReadByte() _, _ = r.ReadByte() if !readPEROctetStreamMatch(r, []byte("McDn"), 4) { return } total, _ := readPERLength(r) for total >= 4 && r.Len() >= 4 { blockType, _ := readLE16(r) blockLen, _ := readLE16(r) if blockLen < 4 || int(blockLen-4) > r.Len() { return } body := make([]byte, int(blockLen-4)) _, _ = io.ReadFull(r, body) total -= blockLen parseGCCServerBlock(blockType, body, info) } } func parseGCCServerBlock(blockType uint16, body []byte, info *MCSProbeInfo) { r := bytes.NewReader(body) switch blockType { case gccSCCore: info.ServerCoreVersion, _ = readLE32(r) case gccSCSecurity: info.ServerEncryption, _ = readLE32(r) info.ServerEncryptionLvl, _ = readLE32(r) case gccSCNet: info.ServerChannelID, _ = readLE16(r) info.ServerChannelCount, _ = readLE16(r) for i := 0; i < int(info.ServerChannelCount) && r.Len() >= 2; i++ { channelID, _ := readLE16(r) info.ServerChannelIDs = append(info.ServerChannelIDs, channelID) // 与 buildGCCClientNetwork 声明的 declaredStaticChannels 按顺序对应,建立 ID→名称映射。 // Logan.Li if i < len(declaredStaticChannels) { if info.ChannelNameByID == nil { info.ChannelNameByID = make(map[uint16]string) } info.ChannelNameByID[channelID] = declaredStaticChannels[i].Name } } case 0x0C04: info.MessageChannelID, _ = readLE16(r) } } type bodyWriter interface { Write([]byte) (int, error) WriteByte(byte) error } func writeBERApplicationTag(w bodyWriter, tag int, size int) { w.WriteByte(0x7f) w.WriteByte(byte(tag)) writeBERLength(w, size) } func readBERApplicationTag(r *bytes.Reader, tag int) error { first, err := r.ReadByte() if err != nil { return err } if first != 0x7f { return fmt.Errorf("MCS BER application tag 头错误: 0x%x", first) } got, err := r.ReadByte() if err != nil { return err } if int(got) != tag { return fmt.Errorf("MCS BER application tag 不匹配: 0x%x", got) } _, err = readBERLength(r) return err } func writeBERDomainParameters(w bodyWriter, maxChannelIDs, maxUserIDs, maxTokenIDs, maxPDUSize int) { body := bytes.Buffer{} writeBERInteger(&body, maxChannelIDs) writeBERInteger(&body, maxUserIDs) writeBERInteger(&body, maxTokenIDs) writeBERInteger(&body, 1) writeBERInteger(&body, 0) writeBERInteger(&body, 1) writeBERInteger(&body, maxPDUSize) writeBERInteger(&body, 2) w.WriteByte(0x30) writeBERLength(w, body.Len()) w.Write(body.Bytes()) } func skipBERDomainParameters(r *bytes.Reader) error { tag, err := r.ReadByte() if err != nil { return err } if tag != 0x30 { return fmt.Errorf("MCS DomainParameters tag 错误: 0x%x", tag) } size, err := readBERLength(r) if err != nil { return err } if size > r.Len() { return io.ErrUnexpectedEOF } _, err = r.Seek(int64(size), io.SeekCurrent) return err } func writeBEROctetString(w bodyWriter, data []byte) { w.WriteByte(0x04) writeBERLength(w, len(data)) w.Write(data) } func readBEROctetString(r *bytes.Reader) ([]byte, error) { tag, err := r.ReadByte() if err != nil { return nil, err } if tag != 0x04 { return nil, fmt.Errorf("MCS octet string tag 错误: 0x%x", tag) } size, err := readBERLength(r) if err != nil { return nil, err } if size > r.Len() { return nil, io.ErrUnexpectedEOF } out := make([]byte, size) _, err = io.ReadFull(r, out) return out, err } func writeBERBoolean(w bodyWriter, value bool) { w.WriteByte(0x01) w.WriteByte(1) if value { w.WriteByte(0xff) return } w.WriteByte(0) } func writeBERInteger(w bodyWriter, value int) { w.WriteByte(0x02) switch { case value <= 0xff: w.WriteByte(1) w.WriteByte(byte(value)) case value <= 0xffff: w.WriteByte(2) var b [2]byte binary.BigEndian.PutUint16(b[:], uint16(value)) w.Write(b[:]) default: w.WriteByte(4) var b [4]byte binary.BigEndian.PutUint32(b[:], uint32(value)) w.Write(b[:]) } } func readBERInteger(r *bytes.Reader) (int, error) { tag, err := r.ReadByte() if err != nil { return 0, err } if tag != 0x02 { return 0, fmt.Errorf("MCS integer tag 错误: 0x%x", tag) } size, err := readBERLength(r) if err != nil { return 0, err } if size > 4 || size > r.Len() { return 0, io.ErrUnexpectedEOF } raw := make([]byte, size) _, _ = io.ReadFull(r, raw) value := 0 for _, b := range raw { value = value<<8 | int(b) } return value, nil } func readBEREnumerated(r *bytes.Reader) (byte, error) { tag, err := r.ReadByte() if err != nil { return 0, err } if tag != 0x0a { return 0, fmt.Errorf("MCS enumerated tag 错误: 0x%x", tag) } size, err := readBERLength(r) if err != nil { return 0, err } if size != 1 { return 0, fmt.Errorf("MCS enumerated 长度错误: %d", size) } return r.ReadByte() } func writeBERLength(w bodyWriter, size int) { if size > 0x7f { w.WriteByte(0x82) var b [2]byte binary.BigEndian.PutUint16(b[:], uint16(size)) w.Write(b[:]) return } w.WriteByte(byte(size)) } func readBERLength(r *bytes.Reader) (int, error) { first, err := r.ReadByte() if err != nil { return 0, err } if first&0x80 == 0 { return int(first), nil } count := int(first & 0x7f) if count == 0 || count > 2 || count > r.Len() { return 0, fmt.Errorf("BER length 字节数非法: %d", count) } value := 0 for i := 0; i < count; i++ { b, _ := r.ReadByte() value = value<<8 | int(b) } return value, nil } func writePERChoice(w bodyWriter, choice byte) { w.WriteByte(choice) } func writePERInteger(w bodyWriter, value int) { if value <= 0xff { writePERLength(w, 1) w.WriteByte(byte(value)) return } if value <= 0xffff { writePERLength(w, 2) var b [2]byte binary.BigEndian.PutUint16(b[:], uint16(value)) w.Write(b[:]) return } writePERLength(w, 4) var b [4]byte binary.BigEndian.PutUint32(b[:], uint32(value)) w.Write(b[:]) } func writePERObjectIdentifier(w bodyWriter, oid []byte) { w.WriteByte(5) w.WriteByte((oid[0] << 4) | (oid[1] & 0x0f)) w.Write(oid[2:6]) } func readPERObjectIdentifier(r *bytes.Reader, oid []byte) bool { size, err := r.ReadByte() if err != nil || size != 5 || r.Len() < 5 { return false } raw := make([]byte, 5) _, _ = io.ReadFull(r, raw) got := []byte{raw[0] >> 4, raw[0] & 0x0f, raw[1], raw[2], raw[3], raw[4]} return bytes.Equal(got, oid) } func writePERLength(w bodyWriter, size int) { if size > 0x7f { var b [2]byte binary.BigEndian.PutUint16(b[:], uint16(size|0x8000)) w.Write(b[:]) return } w.WriteByte(byte(size)) } func readPERLength(r *bytes.Reader) (uint16, error) { first, err := r.ReadByte() if err != nil { return 0, err } if first&0x80 == 0 { return uint16(first), nil } next, err := r.ReadByte() if err != nil { return 0, err } return uint16(first&0x7f)<<8 | uint16(next), nil } func writePERNumericString(w bodyWriter, s string, minValue int) { length := len(s) encodedLength := minValue if length >= minValue { encodedLength = length - minValue } writePERLength(w, encodedLength) for i := 0; i < length; i += 2 { left := (s[i] - '0') % 10 right := byte(0) if i+1 < length { right = (s[i+1] - '0') % 10 } w.WriteByte((left << 4) | right) } } func writePEROctetStream(w bodyWriter, data []byte, minValue int) { encodedLength := minValue if len(data) >= minValue { encodedLength = len(data) - minValue } writePERLength(w, encodedLength) w.Write(data) } func readPEROctetStreamMatch(r *bytes.Reader, expected []byte, minValue int) bool { encodedLength, err := readPERLength(r) if err != nil { return false } size := int(encodedLength) + minValue if size != len(expected) || size > r.Len() { return false } raw := make([]byte, size) _, _ = io.ReadFull(r, raw) return bytes.Equal(raw, expected) } func readPERInteger(r *bytes.Reader) (uint32, error) { size, err := readPERLength(r) if err != nil { return 0, err } if int(size) > r.Len() { return 0, io.ErrUnexpectedEOF } switch size { case 1: b, _ := r.ReadByte() return uint32(b), nil case 2: return readBE16(r) case 4: return readBE32(r) default: _, _ = r.Seek(int64(size), io.SeekCurrent) return 0, nil } } func fixedUTF16LE(value string, size int) []byte { out := make([]byte, size) encoded := utf16.Encode([]rune(value)) for i, r := range encoded { offset := i * 2 if offset+1 >= len(out) { break } binary.LittleEndian.PutUint16(out[offset:offset+2], r) } return out } func utf16LENull(value string) []byte { out := bytes.Buffer{} for _, r := range utf16.Encode([]rune(value)) { writeLE16(&out, r) } writeLE16(&out, 0) return out.Bytes() } func writeLE16(w io.Writer, value uint16) { var b [2]byte binary.LittleEndian.PutUint16(b[:], value) _, _ = w.Write(b[:]) } func writeLE32(w io.Writer, value uint32) { var b [4]byte binary.LittleEndian.PutUint32(b[:], value) _, _ = w.Write(b[:]) } func readLE16(r io.Reader) (uint16, error) { var b [2]byte _, err := io.ReadFull(r, b[:]) return binary.LittleEndian.Uint16(b[:]), err } func readLE32(r io.Reader) (uint32, error) { var b [4]byte _, err := io.ReadFull(r, b[:]) return binary.LittleEndian.Uint32(b[:]), err } func readBE16(r io.Reader) (uint32, error) { var b [2]byte _, err := io.ReadFull(r, b[:]) return uint32(binary.BigEndian.Uint16(b[:])), err } func readBE32(r io.Reader) (uint32, error) { var b [4]byte _, err := io.ReadFull(r, b[:]) return binary.BigEndian.Uint32(b[:]), err } func writeMCSPDUHeader(w bodyWriter, pduType byte) { w.WriteByte(pduType << 2) } func readMCSPDUHeader(r *bytes.Reader) (byte, error) { header, err := r.ReadByte() if err != nil { return 0, err } return header >> 2, nil } func writeBE16Value(w bodyWriter, value uint16) { var b [2]byte binary.BigEndian.PutUint16(b[:], value) w.Write(b[:]) } func readBE16Value(r io.Reader) (uint16, error) { var b [2]byte _, err := io.ReadFull(r, b[:]) return binary.BigEndian.Uint16(b[:]), err }