mirror of
https://github.com/yuanyuanxiang/SimpleRemoter.git
synced 2026-01-21 23:13:08 +08:00
Feature: Add Go TCP server framework
This commit is contained in:
161
server/go/protocol/codec.go
Normal file
161
server/go/protocol/codec.go
Normal file
@@ -0,0 +1,161 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"github.com/yuanyuanxiang/SimpleRemoter/server/go/connection"
|
||||
)
|
||||
|
||||
// Codec handles encoding/decoding and compression
|
||||
type Codec struct {
|
||||
encoder *zstd.Encoder
|
||||
decoder *zstd.Decoder
|
||||
}
|
||||
|
||||
// NewCodec creates a new codec
|
||||
func NewCodec() *Codec {
|
||||
encoder, err := zstd.NewWriter(nil, zstd.WithEncoderLevel(zstd.SpeedDefault))
|
||||
if err != nil {
|
||||
panic("failed to create zstd encoder: " + err.Error())
|
||||
}
|
||||
decoder, err := zstd.NewReader(nil)
|
||||
if err != nil {
|
||||
panic("failed to create zstd decoder: " + err.Error())
|
||||
}
|
||||
|
||||
return &Codec{
|
||||
encoder: encoder,
|
||||
decoder: decoder,
|
||||
}
|
||||
}
|
||||
|
||||
// Compress compresses data using the appropriate method
|
||||
func (c *Codec) Compress(ctx *connection.Context, data []byte) ([]byte, error) {
|
||||
switch ctx.CompressMethod {
|
||||
case connection.CompressNone:
|
||||
return data, nil
|
||||
case connection.CompressZstd:
|
||||
return c.encoder.EncodeAll(data, nil), nil
|
||||
default:
|
||||
// Default to zstd
|
||||
return c.encoder.EncodeAll(data, nil), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Decompress decompresses data using the appropriate method
|
||||
func (c *Codec) Decompress(ctx *connection.Context, data []byte, origLen uint32) ([]byte, error) {
|
||||
switch ctx.CompressMethod {
|
||||
case connection.CompressNone:
|
||||
// No compression, return as-is
|
||||
result := make([]byte, len(data))
|
||||
copy(result, data)
|
||||
return result, nil
|
||||
case connection.CompressZstd:
|
||||
result := make([]byte, 0, origLen)
|
||||
return c.decoder.DecodeAll(data, result)
|
||||
default:
|
||||
// Try zstd by default
|
||||
result := make([]byte, 0, origLen)
|
||||
return c.decoder.DecodeAll(data, result)
|
||||
}
|
||||
}
|
||||
|
||||
// Encode encodes data after compression (before sending) - Encoder2
|
||||
func (c *Codec) Encode(ctx *connection.Context, data []byte) {
|
||||
// This is Encoder2 - applied after compression
|
||||
switch ctx.FlagType {
|
||||
case connection.FlagHello, connection.FlagHell:
|
||||
// XOREncoder16 - needs param from header
|
||||
// For now, skip encoding on send since we need the header params
|
||||
case connection.FlagFuck:
|
||||
// No encoding after compression for FUCK
|
||||
}
|
||||
}
|
||||
|
||||
// Decode decodes data before decompression (after receiving) - Encoder2
|
||||
func (c *Codec) Decode(ctx *connection.Context, data []byte) {
|
||||
// This is Encoder2 - applied before decompression
|
||||
// XOREncoder16 for HELL/HELLO protocols
|
||||
if ctx.FlagType == connection.FlagHell || ctx.FlagType == connection.FlagHello {
|
||||
// Get k1, k2 from stored header params
|
||||
if len(ctx.HeaderParams) >= 8 {
|
||||
k1 := ctx.HeaderParams[6]
|
||||
k2 := ctx.HeaderParams[7]
|
||||
if k1 != 0 || k2 != 0 {
|
||||
xorDecoder16(data, k1, k2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EncodeData encodes data before compression - Encoder
|
||||
func (c *Codec) EncodeData(ctx *connection.Context, data []byte) {
|
||||
// This is Encoder - applied before compression
|
||||
// Default encoder does nothing
|
||||
}
|
||||
|
||||
// DecodeData decodes data after decompression - Encoder
|
||||
func (c *Codec) DecodeData(ctx *connection.Context, data []byte) {
|
||||
// This is Encoder - applied after decompression
|
||||
// Default encoder does nothing
|
||||
}
|
||||
|
||||
// xorDecoder16 implements XOREncoder16.decrypt_internal
|
||||
func xorDecoder16(data []byte, k1, k2 byte) {
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
key := (uint16(k1) << 8) | uint16(k2)
|
||||
dataLen := len(data)
|
||||
|
||||
// Reverse two rounds of pseudo-random swaps
|
||||
for round := 1; round >= 0; round-- {
|
||||
for i := dataLen - 1; i >= 0; i-- {
|
||||
j := int(pseudoRandom(key, i+round*100)) % dataLen
|
||||
data[i], data[j] = data[j], data[i]
|
||||
}
|
||||
}
|
||||
|
||||
// XOR decode
|
||||
for i := 0; i < dataLen; i++ {
|
||||
data[i] ^= (k1 + byte(i*13)) ^ (k2 ^ byte(i<<1))
|
||||
}
|
||||
}
|
||||
|
||||
// xorEncoder16 implements XOREncoder16.encrypt_internal
|
||||
func xorEncoder16(data []byte, k1, k2 byte) {
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
key := (uint16(k1) << 8) | uint16(k2)
|
||||
dataLen := len(data)
|
||||
|
||||
// XOR encode
|
||||
for i := 0; i < dataLen; i++ {
|
||||
data[i] ^= (k1 + byte(i*13)) ^ (k2 ^ byte(i<<1))
|
||||
}
|
||||
|
||||
// Two rounds of pseudo-random swaps
|
||||
for round := 0; round < 2; round++ {
|
||||
for i := 0; i < dataLen; i++ {
|
||||
j := int(pseudoRandom(key, i+round*100)) % dataLen
|
||||
data[i], data[j] = data[j], data[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pseudoRandom matches the C++ pseudo_random function
|
||||
func pseudoRandom(seed uint16, index int) uint16 {
|
||||
return ((seed ^ uint16(index*251+97)) * 733) ^ (seed >> 3)
|
||||
}
|
||||
|
||||
// Close releases resources
|
||||
func (c *Codec) Close() {
|
||||
if c.encoder != nil {
|
||||
c.encoder.Close()
|
||||
}
|
||||
if c.decoder != nil {
|
||||
c.decoder.Close()
|
||||
}
|
||||
}
|
||||
202
server/go/protocol/commands.go
Normal file
202
server/go/protocol/commands.go
Normal file
@@ -0,0 +1,202 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// gbkToUTF8 converts GBK encoded bytes to UTF-8 string
|
||||
func gbkToUTF8(data []byte) string {
|
||||
// Find the first null byte and truncate there
|
||||
if idx := bytes.IndexByte(data, 0); idx >= 0 {
|
||||
data = data[:idx]
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Try to decode as GBK
|
||||
reader := transform.NewReader(bytes.NewReader(data), simplifiedchinese.GBK.NewDecoder())
|
||||
buf := new(bytes.Buffer)
|
||||
_, err := buf.ReadFrom(reader)
|
||||
if err != nil {
|
||||
// If GBK decoding fails, try treating as UTF-8 or ASCII
|
||||
return cleanString(string(data))
|
||||
}
|
||||
return cleanString(buf.String())
|
||||
}
|
||||
|
||||
// cleanString removes non-printable characters except common whitespace
|
||||
func cleanString(s string) string {
|
||||
var result strings.Builder
|
||||
for _, r := range s {
|
||||
if r >= 32 || r == '\t' || r == '\n' || r == '\r' {
|
||||
result.WriteRune(r)
|
||||
}
|
||||
}
|
||||
return strings.TrimSpace(result.String())
|
||||
}
|
||||
|
||||
// Command tokens - matching the C++ definitions
|
||||
const (
|
||||
// Server -> Client commands
|
||||
CommandActived byte = 0 // COMMAND_ACTIVED
|
||||
CommandBye byte = 204 // COMMAND_BYE - disconnect
|
||||
CommandHeartbeat byte = 216 // CMD_HEARTBEAT_ACK
|
||||
|
||||
// Client -> Server tokens
|
||||
TokenAuth byte = 100 // TOKEN_AUTH - authorization required
|
||||
TokenHeartbeat byte = 101 // TOKEN_HEARTBEAT
|
||||
TokenLogin byte = 102 // TOKEN_LOGIN - login packet
|
||||
)
|
||||
|
||||
// LOGIN_INFOR structure size and offsets (matching C++ struct with default alignment)
|
||||
// Note: C++ struct uses default alignment (4-byte for uint32/int)
|
||||
const (
|
||||
LoginInfoSize = 980 // Total size of LOGIN_INFOR struct (with alignment padding)
|
||||
|
||||
// Field offsets (with alignment padding)
|
||||
OffsetToken = 0 // 1 byte (unsigned char)
|
||||
OffsetOsVerInfoEx = 1 // 156 bytes (char[156])
|
||||
// 3 bytes padding here to align dwCPUMHz to 4-byte boundary
|
||||
OffsetCPUMHz = 160 // 4 bytes (unsigned int) - aligned to 4
|
||||
OffsetModuleVersion = 164 // 24 bytes (char[24])
|
||||
OffsetPCName = 188 // 240 bytes (char[240])
|
||||
OffsetMasterID = 428 // 20 bytes (char[20])
|
||||
OffsetWebCamExist = 448 // 4 bytes (int) - aligned to 4
|
||||
OffsetSpeed = 452 // 4 bytes (unsigned int)
|
||||
OffsetStartTime = 456 // 20 bytes (char[20])
|
||||
OffsetReserved = 476 // 512 bytes (char[512])
|
||||
)
|
||||
|
||||
// LoginInfo represents client login information
|
||||
type LoginInfo struct {
|
||||
Token byte
|
||||
OsVerInfo string // OS version info
|
||||
CPUMHz uint32
|
||||
ModuleVersion string
|
||||
PCName string // Computer name
|
||||
MasterID string
|
||||
WebCamExist bool
|
||||
Speed uint32
|
||||
StartTime string
|
||||
Reserved string // Contains additional info separated by |
|
||||
}
|
||||
|
||||
// ParseLoginInfo parses LOGIN_INFOR from data
|
||||
func ParseLoginInfo(data []byte) (*LoginInfo, error) {
|
||||
if len(data) < 100 { // Minimum size check
|
||||
return nil, ErrInvalidData
|
||||
}
|
||||
|
||||
info := &LoginInfo{
|
||||
Token: data[0],
|
||||
}
|
||||
|
||||
// Parse OS version info (offset 1, 156 bytes)
|
||||
// The C++ client fills this with a readable string like "Windows 10" via getSystemName()
|
||||
if len(data) >= OffsetOsVerInfoEx+156 {
|
||||
info.OsVerInfo = parseOsVersionInfo(data[OffsetOsVerInfoEx : OffsetOsVerInfoEx+156])
|
||||
}
|
||||
|
||||
// Parse CPU MHz (offset 160, 4 bytes)
|
||||
if len(data) >= OffsetCPUMHz+4 {
|
||||
info.CPUMHz = binary.LittleEndian.Uint32(data[OffsetCPUMHz:])
|
||||
}
|
||||
|
||||
// Parse module version (offset 164, 24 bytes)
|
||||
// This contains date string like "Dec 19 2025"
|
||||
if len(data) >= OffsetModuleVersion+24 {
|
||||
info.ModuleVersion = gbkToUTF8(data[OffsetModuleVersion : OffsetModuleVersion+24])
|
||||
}
|
||||
|
||||
// Parse PC name (offset 188, 240 bytes)
|
||||
if len(data) >= OffsetPCName+240 {
|
||||
info.PCName = gbkToUTF8(data[OffsetPCName : OffsetPCName+240])
|
||||
}
|
||||
|
||||
// Parse Master ID (offset 428, 20 bytes)
|
||||
if len(data) >= OffsetMasterID+20 {
|
||||
info.MasterID = gbkToUTF8(data[OffsetMasterID : OffsetMasterID+20])
|
||||
}
|
||||
|
||||
// Parse WebCam exist (offset 448, 4 bytes)
|
||||
if len(data) >= OffsetWebCamExist+4 {
|
||||
info.WebCamExist = binary.LittleEndian.Uint32(data[OffsetWebCamExist:]) != 0
|
||||
}
|
||||
|
||||
// Parse Speed (offset 452, 4 bytes)
|
||||
if len(data) >= OffsetSpeed+4 {
|
||||
info.Speed = binary.LittleEndian.Uint32(data[OffsetSpeed:])
|
||||
}
|
||||
|
||||
// Parse Start time (offset 456, 20 bytes)
|
||||
if len(data) >= OffsetStartTime+20 {
|
||||
info.StartTime = gbkToUTF8(data[OffsetStartTime : OffsetStartTime+20])
|
||||
}
|
||||
|
||||
// Parse Reserved (offset 476, 512 bytes) - contains additional info
|
||||
if len(data) >= OffsetReserved+512 {
|
||||
info.Reserved = gbkToUTF8(data[OffsetReserved : OffsetReserved+512])
|
||||
} else if len(data) > OffsetReserved {
|
||||
info.Reserved = gbkToUTF8(data[OffsetReserved:])
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// parseOsVersionInfo parses the OS version info field
|
||||
// The C++ client fills this with a readable string like "Windows 10" via getSystemName()
|
||||
func parseOsVersionInfo(data []byte) string {
|
||||
return gbkToUTF8(data)
|
||||
}
|
||||
|
||||
// ParseReserved parses the reserved field into a slice of strings
|
||||
func (info *LoginInfo) ParseReserved() []string {
|
||||
if info.Reserved == "" {
|
||||
return nil
|
||||
}
|
||||
return strings.Split(info.Reserved, "|")
|
||||
}
|
||||
|
||||
// GetReservedField returns a specific field from reserved data by index
|
||||
// Fields: ClientType(0), SystemBits(1), CPU(2), Memory(3), FilePath(4),
|
||||
// Reserved(5), InstallTime(6), InstallInfo(7), ProgramBits(8), ExpiredDate(9),
|
||||
// ClientLoc(10), ClientPubIP(11), ExeVersion(12), Username(13), IsAdmin(14)
|
||||
func (info *LoginInfo) GetReservedField(index int) string {
|
||||
fields := info.ParseReserved()
|
||||
if index >= 0 && index < len(fields) {
|
||||
return fields[index]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Validation structure for TOKEN_AUTH
|
||||
type Validation struct {
|
||||
From string // Start date
|
||||
To string // End date
|
||||
Admin string // Admin address
|
||||
Port int // Admin port
|
||||
Checksum string // Reserved field
|
||||
}
|
||||
|
||||
// BuildValidation creates a validation response
|
||||
func BuildValidation(days float64, admin string, port int) []byte {
|
||||
// This would build the validation structure
|
||||
// For now, return a simple structure
|
||||
data := make([]byte, 160) // Size of Validation struct
|
||||
data[0] = TokenAuth
|
||||
|
||||
// Fill in fields...
|
||||
// From: 20 bytes
|
||||
// To: 20 bytes
|
||||
// Admin: 100 bytes
|
||||
// Port: 4 bytes
|
||||
// Checksum: 16 bytes
|
||||
|
||||
return data
|
||||
}
|
||||
267
server/go/protocol/header.go
Normal file
267
server/go/protocol/header.go
Normal file
@@ -0,0 +1,267 @@
|
||||
package protocol
|
||||
|
||||
// Header encoding/decoding functions
|
||||
// Ported from common/header.h and common/encfuncs.h
|
||||
|
||||
const (
|
||||
MsgHeader = "HELL"
|
||||
FlagCompLen = 4
|
||||
FlagLength = 8
|
||||
HdrLength = FlagLength + 8 // FLAG_LENGTH + 2 * sizeof(uint32)
|
||||
MinComLen = 12
|
||||
)
|
||||
|
||||
// HeaderEncType represents the encryption method used for header
|
||||
type HeaderEncType int
|
||||
|
||||
const (
|
||||
HeaderEncUnknown HeaderEncType = -1
|
||||
HeaderEncNone HeaderEncType = 0
|
||||
HeaderEncV0 HeaderEncType = 1
|
||||
HeaderEncV1 HeaderEncType = 2
|
||||
HeaderEncV2 HeaderEncType = 3
|
||||
HeaderEncV3 HeaderEncType = 4
|
||||
HeaderEncV4 HeaderEncType = 5
|
||||
HeaderEncV5 HeaderEncType = 6
|
||||
HeaderEncV6 HeaderEncType = 7
|
||||
)
|
||||
|
||||
// DecryptFunc is the function signature for header decryption
|
||||
type DecryptFunc func(data []byte, key byte)
|
||||
|
||||
// defaultDecrypt does nothing (no encryption)
|
||||
func defaultDecrypt(data []byte, key byte) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
// decrypt is the default encryption method (V0)
|
||||
func decrypt(data []byte, key byte) {
|
||||
if key == 0 {
|
||||
return
|
||||
}
|
||||
for i := 0; i < len(data); i++ {
|
||||
k := key ^ byte(i*31)
|
||||
value := int(data[i])
|
||||
switch i % 4 {
|
||||
case 0:
|
||||
value -= int(k)
|
||||
case 1:
|
||||
value = value ^ int(k)
|
||||
case 2:
|
||||
value += int(k)
|
||||
case 3:
|
||||
value = ^value ^ int(k)
|
||||
}
|
||||
data[i] = byte(value & 0xFF)
|
||||
}
|
||||
}
|
||||
|
||||
// decryptV1 - alternating add/subtract
|
||||
func decryptV1(data []byte, key byte) {
|
||||
for i := 0; i < len(data); i++ {
|
||||
if i%2 == 0 {
|
||||
data[i] = data[i] - key
|
||||
} else {
|
||||
data[i] = data[i] + key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// decryptV2 - XOR with rotation
|
||||
func decryptV2(data []byte, key byte) {
|
||||
for i := 0; i < len(data); i++ {
|
||||
if i%2 == 0 {
|
||||
data[i] = (data[i] >> 1) | (data[i] << 7) // rotate right
|
||||
} else {
|
||||
data[i] = (data[i] << 1) | (data[i] >> 7) // rotate left
|
||||
}
|
||||
data[i] ^= key
|
||||
}
|
||||
}
|
||||
|
||||
// decryptV3 - dynamic key with position
|
||||
func decryptV3(data []byte, key byte) {
|
||||
for i := 0; i < len(data); i++ {
|
||||
dynamicKey := key + byte(i%8)
|
||||
switch i % 3 {
|
||||
case 0:
|
||||
data[i] = (data[i] ^ dynamicKey) - dynamicKey
|
||||
case 1:
|
||||
data[i] = (data[i] + dynamicKey) ^ dynamicKey
|
||||
case 2:
|
||||
data[i] = ^data[i] - dynamicKey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// decryptV4 - pseudo-random XOR (symmetric)
|
||||
func decryptV4(data []byte, key byte) {
|
||||
rand := uint16(key)
|
||||
for i := 0; i < len(data); i++ {
|
||||
rand = (rand*13 + 17) % 256
|
||||
data[i] ^= byte(rand)
|
||||
}
|
||||
}
|
||||
|
||||
// decryptV5 - dynamic key with bit shift
|
||||
func decryptV5(data []byte, key byte) {
|
||||
for i := 0; i < len(data); i++ {
|
||||
dynamicKey := (key + byte(i)) ^ 0x55
|
||||
data[i] = ((data[i] - byte(i%7)) ^ (dynamicKey << 3)) - dynamicKey
|
||||
}
|
||||
}
|
||||
|
||||
// decryptV6 - pseudo-random with position (symmetric)
|
||||
func decryptV6(data []byte, key byte) {
|
||||
rand := uint16(key)
|
||||
for i := 0; i < len(data); i++ {
|
||||
rand = (rand*31 + 17) % 256
|
||||
data[i] ^= byte(rand) + byte(i)
|
||||
}
|
||||
}
|
||||
|
||||
// All decrypt methods
|
||||
var decryptMethods = []DecryptFunc{
|
||||
defaultDecrypt,
|
||||
decrypt,
|
||||
decryptV1,
|
||||
decryptV2,
|
||||
decryptV3,
|
||||
decryptV4,
|
||||
decryptV5,
|
||||
decryptV6,
|
||||
}
|
||||
|
||||
// compare decrypts and compares the flag with magic
|
||||
func compare(flag []byte, magic string, length int, dec DecryptFunc, key byte) bool {
|
||||
if len(flag) < MinComLen {
|
||||
return false
|
||||
}
|
||||
|
||||
buf := make([]byte, MinComLen)
|
||||
copy(buf, flag[:MinComLen])
|
||||
dec(buf[:length], key)
|
||||
|
||||
return string(buf[:length]) == magic
|
||||
}
|
||||
|
||||
// CheckHead tries all decryption methods to identify the protocol
|
||||
func CheckHead(flag []byte) (flagType FlagType, encType HeaderEncType, decrypted []byte) {
|
||||
if len(flag) < MinComLen {
|
||||
return FlagUnknown, HeaderEncUnknown, nil
|
||||
}
|
||||
|
||||
for i, method := range decryptMethods {
|
||||
buf := make([]byte, MinComLen)
|
||||
copy(buf, flag[:MinComLen])
|
||||
|
||||
ft := checkHeadWithMethod(buf, method)
|
||||
if ft != FlagUnknown {
|
||||
return ft, HeaderEncType(i), buf
|
||||
}
|
||||
}
|
||||
|
||||
return FlagUnknown, HeaderEncUnknown, nil
|
||||
}
|
||||
|
||||
// checkHeadWithMethod checks the flag with a specific decrypt method
|
||||
func checkHeadWithMethod(flag []byte, dec DecryptFunc) FlagType {
|
||||
// Try HELL (FLAG_HELL)
|
||||
if len(flag) >= FlagLength {
|
||||
buf := make([]byte, MinComLen)
|
||||
copy(buf, flag)
|
||||
key := buf[6]
|
||||
dec(buf[:FlagCompLen], key)
|
||||
if string(buf[:4]) == MsgHeader {
|
||||
copy(flag, buf)
|
||||
return FlagHell
|
||||
}
|
||||
}
|
||||
|
||||
// Try Shine (FLAG_SHINE)
|
||||
if len(flag) >= 5 {
|
||||
buf := make([]byte, MinComLen)
|
||||
copy(buf, flag)
|
||||
dec(buf[:5], 0)
|
||||
if string(buf[:5]) == "Shine" {
|
||||
copy(flag, buf)
|
||||
return FlagShine
|
||||
}
|
||||
}
|
||||
|
||||
// Try <<FUCK>> (FLAG_FUCK)
|
||||
if len(flag) >= 10 {
|
||||
buf := make([]byte, MinComLen)
|
||||
copy(buf, flag)
|
||||
key := buf[9]
|
||||
dec(buf[:8], key)
|
||||
if string(buf[:8]) == "<<FUCK>>" {
|
||||
copy(flag, buf)
|
||||
return FlagFuck
|
||||
}
|
||||
}
|
||||
|
||||
// Try Hello? (FLAG_HELLO)
|
||||
if len(flag) >= 7 {
|
||||
buf := make([]byte, MinComLen)
|
||||
copy(buf, flag)
|
||||
key := buf[6]
|
||||
dec(buf[:6], key)
|
||||
if string(buf[:6]) == "Hello?" {
|
||||
copy(flag, buf)
|
||||
return FlagHello
|
||||
}
|
||||
}
|
||||
|
||||
return FlagUnknown
|
||||
}
|
||||
|
||||
// FlagType represents the protocol type
|
||||
type FlagType int
|
||||
|
||||
const (
|
||||
FlagWinOS FlagType = -1
|
||||
FlagUnknown FlagType = 0
|
||||
FlagShine FlagType = 1
|
||||
FlagFuck FlagType = 2
|
||||
FlagHello FlagType = 3
|
||||
FlagHell FlagType = 4
|
||||
)
|
||||
|
||||
func (f FlagType) String() string {
|
||||
switch f {
|
||||
case FlagWinOS:
|
||||
return "WinOS"
|
||||
case FlagShine:
|
||||
return "Shine"
|
||||
case FlagFuck:
|
||||
return "Fuck"
|
||||
case FlagHello:
|
||||
return "Hello"
|
||||
case FlagHell:
|
||||
return "Hell"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// GetFlagLength returns the flag length for a given flag type
|
||||
func GetFlagLength(ft FlagType) int {
|
||||
switch ft {
|
||||
case FlagShine:
|
||||
return 5
|
||||
case FlagFuck:
|
||||
return 11 // 8 + 3
|
||||
case FlagHello:
|
||||
return 8
|
||||
case FlagHell:
|
||||
return FlagLength
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// GetHeaderLength returns the full header length for a given flag type
|
||||
func GetHeaderLength(ft FlagType) int {
|
||||
return GetFlagLength(ft) + 8
|
||||
}
|
||||
261
server/go/protocol/parser.go
Normal file
261
server/go/protocol/parser.go
Normal file
@@ -0,0 +1,261 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"github.com/yuanyuanxiang/SimpleRemoter/server/go/connection"
|
||||
)
|
||||
|
||||
// Errors
|
||||
var (
|
||||
ErrNeedMore = errors.New("need more data")
|
||||
ErrInvalidData = errors.New("invalid data")
|
||||
ErrUnsupported = errors.New("unsupported protocol")
|
||||
ErrDecompress = errors.New("decompression failed")
|
||||
)
|
||||
|
||||
// Parser handles protocol parsing
|
||||
type Parser struct {
|
||||
codec *Codec
|
||||
}
|
||||
|
||||
// NewParser creates a new parser
|
||||
func NewParser() *Parser {
|
||||
return &Parser{
|
||||
codec: NewCodec(),
|
||||
}
|
||||
}
|
||||
|
||||
// Close releases resources held by the parser
|
||||
func (p *Parser) Close() {
|
||||
if p.codec != nil {
|
||||
p.codec.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// Parse attempts to parse a complete packet from the buffer
|
||||
func (p *Parser) Parse(ctx *connection.Context) ([]byte, error) {
|
||||
buf := ctx.InBuffer
|
||||
|
||||
// Need at least minimum bytes to determine protocol
|
||||
if buf.Len() < MinComLen {
|
||||
return nil, ErrNeedMore
|
||||
}
|
||||
|
||||
// Check if header is already parsed
|
||||
if ctx.FlagType == connection.FlagUnknown {
|
||||
// Try to parse header
|
||||
if err := p.parseHeader(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Now parse the packet
|
||||
return p.parsePacket(ctx)
|
||||
}
|
||||
|
||||
// parseHeader parses the protocol header with obfuscation handling
|
||||
func (p *Parser) parseHeader(ctx *connection.Context) error {
|
||||
buf := ctx.InBuffer
|
||||
header := buf.Peek(MinComLen)
|
||||
if header == nil || len(header) < MinComLen {
|
||||
return ErrNeedMore
|
||||
}
|
||||
|
||||
// Try to decode the header using all encryption methods
|
||||
flagType, encType, decrypted := CheckHead(header)
|
||||
|
||||
if flagType == FlagUnknown {
|
||||
return ErrUnsupported
|
||||
}
|
||||
|
||||
// Store decrypted header params for later use (for XOREncoder16)
|
||||
ctx.HeaderParams = make([]byte, MinComLen)
|
||||
if decrypted != nil {
|
||||
copy(ctx.HeaderParams, decrypted)
|
||||
} else {
|
||||
copy(ctx.HeaderParams, header)
|
||||
}
|
||||
|
||||
// Map protocol FlagType to connection FlagType and set compression method
|
||||
switch flagType {
|
||||
case FlagHell:
|
||||
ctx.FlagType = connection.FlagHell
|
||||
ctx.FlagLen = FlagLength
|
||||
ctx.HeaderLen = ctx.FlagLen + 8
|
||||
ctx.CompressMethod = connection.CompressZstd // HELL uses ZSTD
|
||||
case FlagHello:
|
||||
ctx.FlagType = connection.FlagHello
|
||||
ctx.FlagLen = 8
|
||||
ctx.HeaderLen = ctx.FlagLen + 8
|
||||
ctx.CompressMethod = connection.CompressNone // HELLO uses no compression
|
||||
case FlagShine:
|
||||
ctx.FlagType = connection.FlagShine
|
||||
ctx.FlagLen = 5
|
||||
ctx.HeaderLen = ctx.FlagLen + 8
|
||||
ctx.CompressMethod = connection.CompressZstd // SHINE uses ZSTD
|
||||
case FlagFuck:
|
||||
ctx.FlagType = connection.FlagFuck
|
||||
ctx.FlagLen = 11
|
||||
ctx.HeaderLen = ctx.FlagLen + 8
|
||||
ctx.CompressMethod = connection.CompressZstd // FUCK uses ZSTD
|
||||
default:
|
||||
return ErrUnsupported
|
||||
}
|
||||
|
||||
// Store encryption type for later use
|
||||
ctx.HeaderEncType = int(encType)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// parsePacket parses a complete packet
|
||||
func (p *Parser) parsePacket(ctx *connection.Context) ([]byte, error) {
|
||||
buf := ctx.InBuffer
|
||||
|
||||
// Check if we have enough data for header
|
||||
if buf.Len() < ctx.HeaderLen {
|
||||
return nil, ErrNeedMore
|
||||
}
|
||||
|
||||
// Peek the header to get total length
|
||||
headerData := buf.Peek(ctx.HeaderLen)
|
||||
if headerData == nil {
|
||||
return nil, ErrNeedMore
|
||||
}
|
||||
|
||||
// Decrypt the header first
|
||||
decryptedHeader := make([]byte, len(headerData))
|
||||
copy(decryptedHeader, headerData)
|
||||
|
||||
// Get the encryption key (usually at position 6)
|
||||
var key byte
|
||||
if len(headerData) > 6 {
|
||||
key = headerData[6] // Use original key before decryption
|
||||
}
|
||||
|
||||
// Decrypt flag portion
|
||||
if ctx.HeaderEncType >= 0 && ctx.HeaderEncType < len(decryptMethods) {
|
||||
decryptMethods[ctx.HeaderEncType](decryptedHeader[:FlagCompLen], key)
|
||||
}
|
||||
|
||||
// Read the total length field (after flag)
|
||||
totalLen := binary.LittleEndian.Uint32(decryptedHeader[ctx.FlagLen:])
|
||||
|
||||
// Validate length
|
||||
if totalLen < uint32(ctx.HeaderLen) || totalLen > 10*1024*1024 {
|
||||
return nil, ErrInvalidData
|
||||
}
|
||||
|
||||
// Check if we have the complete packet
|
||||
if buf.Len() < int(totalLen) {
|
||||
return nil, ErrNeedMore
|
||||
}
|
||||
|
||||
// Read the complete packet
|
||||
packet := buf.Read(int(totalLen))
|
||||
if packet == nil {
|
||||
return nil, ErrInvalidData
|
||||
}
|
||||
|
||||
// Decrypt header portion of packet
|
||||
if ctx.HeaderEncType >= 0 && ctx.HeaderEncType < len(decryptMethods) {
|
||||
decryptMethods[ctx.HeaderEncType](packet[:FlagCompLen], key)
|
||||
}
|
||||
|
||||
// Update HeaderParams with this packet's header (for XOREncoder16 k1, k2)
|
||||
if len(packet) >= ctx.FlagLen {
|
||||
ctx.HeaderParams = make([]byte, ctx.HeaderLen)
|
||||
copy(ctx.HeaderParams, packet[:ctx.HeaderLen])
|
||||
}
|
||||
|
||||
// Extract data after header
|
||||
dataStart := ctx.HeaderLen
|
||||
data := packet[dataStart:]
|
||||
|
||||
// Get original length (before compression)
|
||||
var origLen uint32
|
||||
if ctx.FlagType != connection.FlagWinOS {
|
||||
origLen = binary.LittleEndian.Uint32(packet[ctx.FlagLen+4 : ctx.FlagLen+8])
|
||||
}
|
||||
|
||||
// Decode (XOR, etc.) before decompression - this is Encoder2
|
||||
p.codec.Decode(ctx, data)
|
||||
|
||||
// Decompress
|
||||
decompressed, err := p.codec.Decompress(ctx, data, origLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Decode after decompression - this is Encoder
|
||||
p.codec.DecodeData(ctx, decompressed)
|
||||
|
||||
return decompressed, nil
|
||||
}
|
||||
|
||||
// Encode encodes data for sending
|
||||
func (p *Parser) Encode(ctx *connection.Context, data []byte) ([]byte, error) {
|
||||
// Encode before compression
|
||||
encoded := make([]byte, len(data))
|
||||
copy(encoded, data)
|
||||
p.codec.EncodeData(ctx, encoded)
|
||||
|
||||
// Compress
|
||||
compressed, err := p.codec.Compress(ctx, encoded)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Build packet
|
||||
packet := p.buildPacket(ctx, compressed, uint32(len(data)))
|
||||
|
||||
return packet, nil
|
||||
}
|
||||
|
||||
// buildPacket builds a complete packet with header
|
||||
func (p *Parser) buildPacket(ctx *connection.Context, data []byte, origLen uint32) []byte {
|
||||
totalLen := ctx.HeaderLen + len(data)
|
||||
packet := make([]byte, totalLen)
|
||||
|
||||
// Write flag
|
||||
flag := p.getFlag(ctx)
|
||||
copy(packet[:ctx.FlagLen], flag)
|
||||
|
||||
// Write total length
|
||||
binary.LittleEndian.PutUint32(packet[ctx.FlagLen:], uint32(totalLen))
|
||||
|
||||
// Write original length
|
||||
binary.LittleEndian.PutUint32(packet[ctx.FlagLen+4:], origLen)
|
||||
|
||||
// Write data
|
||||
copy(packet[ctx.HeaderLen:], data)
|
||||
|
||||
// Encode after building
|
||||
p.codec.Encode(ctx, packet[ctx.HeaderLen:])
|
||||
|
||||
return packet
|
||||
}
|
||||
|
||||
// getFlag returns the protocol flag bytes
|
||||
func (p *Parser) getFlag(ctx *connection.Context) []byte {
|
||||
switch ctx.FlagType {
|
||||
case connection.FlagHell:
|
||||
flag := make([]byte, FlagLength)
|
||||
copy(flag, []byte(MsgHeader))
|
||||
return flag
|
||||
case connection.FlagHello:
|
||||
flag := make([]byte, 8)
|
||||
copy(flag, []byte("Hello?"))
|
||||
return flag
|
||||
case connection.FlagShine:
|
||||
return []byte("Shine")
|
||||
case connection.FlagFuck:
|
||||
flag := make([]byte, 11)
|
||||
copy(flag, []byte("<<FUCK>>"))
|
||||
return flag
|
||||
default:
|
||||
return make([]byte, ctx.FlagLen)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user