mirror of
https://github.com/yv1ing/ShotRDP.git
synced 2025-09-16 15:10:57 +08:00
516 lines
16 KiB
Go
516 lines
16 KiB
Go
package nla
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/md5"
|
|
"crypto/rc4"
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"time"
|
|
|
|
"ShotRDP/grdp/core"
|
|
"ShotRDP/grdp/glog"
|
|
"github.com/lunixbochs/struc"
|
|
)
|
|
|
|
const (
|
|
WINDOWS_MINOR_VERSION_0 = 0x00
|
|
WINDOWS_MINOR_VERSION_1 = 0x01
|
|
WINDOWS_MINOR_VERSION_2 = 0x02
|
|
WINDOWS_MINOR_VERSION_3 = 0x03
|
|
|
|
WINDOWS_MAJOR_VERSION_5 = 0x05
|
|
WINDOWS_MAJOR_VERSION_6 = 0x06
|
|
NTLMSSP_REVISION_W2K3 = 0x0F
|
|
)
|
|
|
|
const (
|
|
MsvAvEOL = 0x0000
|
|
MsvAvNbComputerName = 0x0001
|
|
MsvAvNbDomainName = 0x0002
|
|
MsvAvDnsComputerName = 0x0003
|
|
MsvAvDnsDomainName = 0x0004
|
|
MsvAvDnsTreeName = 0x0005
|
|
MsvAvFlags = 0x0006
|
|
MsvAvTimestamp = 0x0007
|
|
MsvAvSingleHost = 0x0008
|
|
MsvAvTargetName = 0x0009
|
|
MsvChannelBindings = 0x000A
|
|
)
|
|
|
|
type AVPair struct {
|
|
Id uint16 `struc:"little"`
|
|
Len uint16 `struc:"little,sizeof=Value"`
|
|
Value []byte `struc:"little"`
|
|
}
|
|
|
|
const (
|
|
NTLMSSP_NEGOTIATE_56 = 0x80000000
|
|
NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000
|
|
NTLMSSP_NEGOTIATE_128 = 0x20000000
|
|
NTLMSSP_NEGOTIATE_VERSION = 0x02000000
|
|
NTLMSSP_NEGOTIATE_TARGET_INFO = 0x00800000
|
|
NTLMSSP_REQUEST_NON_NT_SESSION_KEY = 0x00400000
|
|
NTLMSSP_NEGOTIATE_IDENTIFY = 0x00100000
|
|
NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x00080000
|
|
NTLMSSP_TARGET_TYPE_SERVER = 0x00020000
|
|
NTLMSSP_TARGET_TYPE_DOMAIN = 0x00010000
|
|
NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x00008000
|
|
NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED = 0x00002000
|
|
NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED = 0x00001000
|
|
NTLMSSP_NEGOTIATE_NTLM = 0x00000200
|
|
NTLMSSP_NEGOTIATE_LM_KEY = 0x00000080
|
|
NTLMSSP_NEGOTIATE_DATAGRAM = 0x00000040
|
|
NTLMSSP_NEGOTIATE_SEAL = 0x00000020
|
|
NTLMSSP_NEGOTIATE_SIGN = 0x00000010
|
|
NTLMSSP_REQUEST_TARGET = 0x00000004
|
|
NTLM_NEGOTIATE_OEM = 0x00000002
|
|
NTLMSSP_NEGOTIATE_UNICODE = 0x00000001
|
|
)
|
|
|
|
type NVersion struct {
|
|
ProductMajorVersion uint8 `struc:"little"`
|
|
ProductMinorVersion uint8 `struc:"little"`
|
|
ProductBuild uint16 `struc:"little"`
|
|
Reserved [3]byte `struc:"little"`
|
|
NTLMRevisionCurrent uint8 `struc:"little"`
|
|
}
|
|
|
|
func NewNVersion() NVersion {
|
|
return NVersion{
|
|
ProductMajorVersion: WINDOWS_MAJOR_VERSION_6,
|
|
ProductMinorVersion: WINDOWS_MINOR_VERSION_0,
|
|
ProductBuild: 6002,
|
|
NTLMRevisionCurrent: NTLMSSP_REVISION_W2K3,
|
|
}
|
|
}
|
|
|
|
type Message interface {
|
|
Serialize() []byte
|
|
}
|
|
|
|
type NegotiateMessage struct {
|
|
Signature [8]byte `struc:"little"`
|
|
MessageType uint32 `struc:"little"`
|
|
NegotiateFlags uint32 `struc:"little"`
|
|
DomainNameLen uint16 `struc:"little"`
|
|
DomainNameMaxLen uint16 `struc:"little"`
|
|
DomainNameBufferOffset uint32 `struc:"little"`
|
|
WorkstationLen uint16 `struc:"little"`
|
|
WorkstationMaxLen uint16 `struc:"little"`
|
|
WorkstationBufferOffset uint32 `struc:"little"`
|
|
Version NVersion `struc:"little"`
|
|
Payload [32]byte `struc:"skip"`
|
|
}
|
|
|
|
func NewNegotiateMessage() *NegotiateMessage {
|
|
return &NegotiateMessage{
|
|
Signature: [8]byte{'N', 'T', 'L', 'M', 'S', 'S', 'P', 0x00},
|
|
MessageType: 0x00000001,
|
|
}
|
|
}
|
|
|
|
func (m *NegotiateMessage) Serialize() []byte {
|
|
if (m.NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) != 0 {
|
|
m.Version = NewNVersion()
|
|
}
|
|
buff := &bytes.Buffer{}
|
|
struc.Pack(buff, m)
|
|
|
|
return buff.Bytes()
|
|
}
|
|
|
|
type ChallengeMessage struct {
|
|
Signature []byte `struc:"[8]byte"`
|
|
MessageType uint32 `struc:"little"`
|
|
TargetNameLen uint16 `struc:"little"`
|
|
TargetNameMaxLen uint16 `struc:"little"`
|
|
TargetNameBufferOffset uint32 `struc:"little"`
|
|
NegotiateFlags uint32 `struc:"little"`
|
|
ServerChallenge [8]byte `struc:"little"`
|
|
Reserved [8]byte `struc:"little"`
|
|
TargetInfoLen uint16 `struc:"little"`
|
|
TargetInfoMaxLen uint16 `struc:"little"`
|
|
TargetInfoBufferOffset uint32 `struc:"little"`
|
|
Version NVersion `struc:"skip"`
|
|
Payload []byte `struc:"skip"`
|
|
}
|
|
|
|
func (m *ChallengeMessage) Serialize() []byte {
|
|
buff := &bytes.Buffer{}
|
|
struc.Pack(buff, m)
|
|
if (m.NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) != 0 {
|
|
struc.Pack(buff, m.Version)
|
|
}
|
|
buff.Write(m.Payload)
|
|
return buff.Bytes()
|
|
}
|
|
|
|
func NewChallengeMessage() *ChallengeMessage {
|
|
return &ChallengeMessage{
|
|
Signature: []byte{'N', 'T', 'L', 'M', 'S', 'S', 'P', 0x00},
|
|
MessageType: 0x00000002,
|
|
}
|
|
}
|
|
|
|
// total len - payload len
|
|
func (m *ChallengeMessage) BaseLen() uint32 {
|
|
return 56
|
|
}
|
|
|
|
func (m *ChallengeMessage) getTargetInfo() []byte {
|
|
if m.TargetInfoLen == 0 {
|
|
return make([]byte, 0)
|
|
}
|
|
offset := m.BaseLen()
|
|
start := m.TargetInfoBufferOffset - offset
|
|
return m.Payload[start : start+uint32(m.TargetInfoLen)]
|
|
}
|
|
func (m *ChallengeMessage) getTargetName() []byte {
|
|
if m.TargetNameLen == 0 {
|
|
return make([]byte, 0)
|
|
}
|
|
offset := m.BaseLen()
|
|
start := m.TargetNameBufferOffset - offset
|
|
return m.Payload[start : start+uint32(m.TargetNameLen)]
|
|
}
|
|
func (m *ChallengeMessage) getTargetInfoTimestamp(data []byte) []byte {
|
|
r := bytes.NewReader(data)
|
|
for r.Len() > 0 {
|
|
avPair := &AVPair{}
|
|
struc.Unpack(r, avPair)
|
|
if avPair.Id == MsvAvTimestamp {
|
|
return avPair.Value
|
|
}
|
|
|
|
if avPair.Id == MsvAvEOL {
|
|
break
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type AuthenticateMessage struct {
|
|
Signature [8]byte
|
|
MessageType uint32 `struc:"little"`
|
|
LmChallengeResponseLen uint16 `struc:"little"`
|
|
LmChallengeResponseMaxLen uint16 `struc:"little"`
|
|
LmChallengeResponseBufferOffset uint32 `struc:"little"`
|
|
NtChallengeResponseLen uint16 `struc:"little"`
|
|
NtChallengeResponseMaxLen uint16 `struc:"little"`
|
|
NtChallengeResponseBufferOffset uint32 `struc:"little"`
|
|
DomainNameLen uint16 `struc:"little"`
|
|
DomainNameMaxLen uint16 `struc:"little"`
|
|
DomainNameBufferOffset uint32 `struc:"little"`
|
|
UserNameLen uint16 `struc:"little"`
|
|
UserNameMaxLen uint16 `struc:"little"`
|
|
UserNameBufferOffset uint32 `struc:"little"`
|
|
WorkstationLen uint16 `struc:"little"`
|
|
WorkstationMaxLen uint16 `struc:"little"`
|
|
WorkstationBufferOffset uint32 `struc:"little"`
|
|
EncryptedRandomSessionLen uint16 `struc:"little"`
|
|
EncryptedRandomSessionMaxLen uint16 `struc:"little"`
|
|
EncryptedRandomSessionBufferOffset uint32 `struc:"little"`
|
|
NegotiateFlags uint32 `struc:"little"`
|
|
Version NVersion `struc:"little"`
|
|
MIC [16]byte `struc:"little"`
|
|
Payload []byte `struc:"skip"`
|
|
}
|
|
|
|
func (m *AuthenticateMessage) BaseLen() uint32 {
|
|
return 88
|
|
}
|
|
|
|
func NewAuthenticateMessage(negFlag uint32, domain, user, workstation []byte,
|
|
lmchallResp, ntchallResp, enRandomSessKey []byte) *AuthenticateMessage {
|
|
msg := &AuthenticateMessage{
|
|
Signature: [8]byte{'N', 'T', 'L', 'M', 'S', 'S', 'P', 0x00},
|
|
MessageType: 0x00000003,
|
|
NegotiateFlags: negFlag,
|
|
}
|
|
payloadBuff := &bytes.Buffer{}
|
|
|
|
msg.LmChallengeResponseLen = uint16(len(lmchallResp))
|
|
msg.LmChallengeResponseMaxLen = msg.LmChallengeResponseLen
|
|
msg.LmChallengeResponseBufferOffset = msg.BaseLen()
|
|
payloadBuff.Write(lmchallResp)
|
|
|
|
msg.NtChallengeResponseLen = uint16(len(ntchallResp))
|
|
msg.NtChallengeResponseMaxLen = msg.NtChallengeResponseLen
|
|
msg.NtChallengeResponseBufferOffset = msg.LmChallengeResponseBufferOffset + uint32(msg.LmChallengeResponseLen)
|
|
payloadBuff.Write(ntchallResp)
|
|
|
|
msg.DomainNameLen = uint16(len(domain))
|
|
msg.DomainNameMaxLen = msg.DomainNameLen
|
|
msg.DomainNameBufferOffset = msg.NtChallengeResponseBufferOffset + uint32(msg.NtChallengeResponseLen)
|
|
payloadBuff.Write(domain)
|
|
|
|
msg.UserNameLen = uint16(len(user))
|
|
msg.UserNameMaxLen = msg.UserNameLen
|
|
msg.UserNameBufferOffset = msg.DomainNameBufferOffset + uint32(msg.DomainNameLen)
|
|
payloadBuff.Write(user)
|
|
|
|
msg.WorkstationLen = uint16(len(workstation))
|
|
msg.WorkstationMaxLen = msg.WorkstationLen
|
|
msg.WorkstationBufferOffset = msg.UserNameBufferOffset + uint32(msg.UserNameLen)
|
|
payloadBuff.Write(workstation)
|
|
|
|
msg.EncryptedRandomSessionLen = uint16(len(enRandomSessKey))
|
|
msg.EncryptedRandomSessionMaxLen = msg.EncryptedRandomSessionLen
|
|
msg.EncryptedRandomSessionBufferOffset = msg.WorkstationBufferOffset + uint32(msg.WorkstationLen)
|
|
payloadBuff.Write(enRandomSessKey)
|
|
|
|
if (msg.NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) != 0 {
|
|
msg.Version = NewNVersion()
|
|
}
|
|
msg.Payload = payloadBuff.Bytes()
|
|
|
|
return msg
|
|
}
|
|
|
|
func (m *AuthenticateMessage) Serialize() []byte {
|
|
buff := &bytes.Buffer{}
|
|
struc.Pack(buff, m)
|
|
buff.Write(m.Payload)
|
|
return buff.Bytes()
|
|
}
|
|
|
|
type NTLMv2 struct {
|
|
domain string
|
|
user string
|
|
password string
|
|
respKeyNT []byte
|
|
respKeyLM []byte
|
|
negotiateMessage *NegotiateMessage
|
|
challengeMessage *ChallengeMessage
|
|
authenticateMessage *AuthenticateMessage
|
|
enableUnicode bool
|
|
}
|
|
|
|
func NewNTLMv2(domain, user, password string) *NTLMv2 {
|
|
return &NTLMv2{
|
|
domain: domain,
|
|
user: user,
|
|
password: password,
|
|
respKeyNT: NTOWFv2(password, user, domain),
|
|
respKeyLM: LMOWFv2(password, user, domain),
|
|
}
|
|
}
|
|
|
|
// generate first handshake messgae
|
|
func (n *NTLMv2) GetNegotiateMessage() *NegotiateMessage {
|
|
negoMsg := NewNegotiateMessage()
|
|
negoMsg.NegotiateFlags = NTLMSSP_NEGOTIATE_KEY_EXCH |
|
|
NTLMSSP_NEGOTIATE_128 |
|
|
NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY |
|
|
NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
|
|
NTLMSSP_NEGOTIATE_NTLM |
|
|
NTLMSSP_NEGOTIATE_SEAL |
|
|
NTLMSSP_NEGOTIATE_SIGN |
|
|
NTLMSSP_REQUEST_TARGET |
|
|
NTLMSSP_NEGOTIATE_UNICODE
|
|
n.negotiateMessage = negoMsg
|
|
return n.negotiateMessage
|
|
}
|
|
|
|
// process NTLMv2 Authenticate hash
|
|
func (n *NTLMv2) ComputeResponseV2(respKeyNT, respKeyLM, serverChallenge, clientChallenge,
|
|
timestamp, serverInfo []byte) (ntChallResp, lmChallResp, SessBaseKey []byte) {
|
|
|
|
tempBuff := &bytes.Buffer{}
|
|
tempBuff.Write([]byte{0x01, 0x01}) // Responser version, HiResponser version
|
|
tempBuff.Write([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
|
tempBuff.Write(timestamp)
|
|
tempBuff.Write(clientChallenge)
|
|
tempBuff.Write([]byte{0x00, 0x00, 0x00, 0x00})
|
|
tempBuff.Write(serverInfo)
|
|
tempBuff.Write([]byte{0x00, 0x00, 0x00, 0x00})
|
|
|
|
ntBuf := bytes.NewBuffer(serverChallenge)
|
|
ntBuf.Write(tempBuff.Bytes())
|
|
ntProof := HMAC_MD5(respKeyNT, ntBuf.Bytes())
|
|
|
|
ntChallResp = make([]byte, 0, len(ntProof)+tempBuff.Len())
|
|
ntChallResp = append(ntChallResp, ntProof...)
|
|
ntChallResp = append(ntChallResp, tempBuff.Bytes()...)
|
|
|
|
lmBuf := bytes.NewBuffer(serverChallenge)
|
|
lmBuf.Write(clientChallenge)
|
|
lmChallResp = HMAC_MD5(respKeyLM, lmBuf.Bytes())
|
|
lmChallResp = append(lmChallResp, clientChallenge...)
|
|
|
|
SessBaseKey = HMAC_MD5(respKeyNT, ntProof)
|
|
return
|
|
}
|
|
|
|
func MIC(exportedSessionKey []byte, negotiateMessage, challengeMessage, authenticateMessage Message) []byte {
|
|
buff := bytes.Buffer{}
|
|
buff.Write(negotiateMessage.Serialize())
|
|
buff.Write(challengeMessage.Serialize())
|
|
buff.Write(authenticateMessage.Serialize())
|
|
return HMAC_MD5(exportedSessionKey, buff.Bytes())
|
|
}
|
|
|
|
func concat(bs ...[]byte) []byte {
|
|
return bytes.Join(bs, nil)
|
|
}
|
|
|
|
var (
|
|
clientSigning = concat([]byte("session key to client-to-server signing key magic constant"), []byte{0x00})
|
|
serverSigning = concat([]byte("session key to server-to-client signing key magic constant"), []byte{0x00})
|
|
clientSealing = concat([]byte("session key to client-to-server sealing key magic constant"), []byte{0x00})
|
|
serverSealing = concat([]byte("session key to server-to-client sealing key magic constant"), []byte{0x00})
|
|
)
|
|
|
|
func (n *NTLMv2) GetAuthenticateMessage(s []byte) (*AuthenticateMessage, *NTLMv2Security) {
|
|
challengeMsg := &ChallengeMessage{}
|
|
r := bytes.NewReader(s)
|
|
err := struc.Unpack(r, challengeMsg)
|
|
if err != nil {
|
|
glog.Error("read challengeMsg", err)
|
|
return nil, nil
|
|
}
|
|
if challengeMsg.NegotiateFlags&NTLMSSP_NEGOTIATE_VERSION != 0 {
|
|
version := NVersion{}
|
|
err := struc.Unpack(r, &version)
|
|
if err != nil {
|
|
glog.Error("read version", err)
|
|
return nil, nil
|
|
}
|
|
challengeMsg.Version = version
|
|
}
|
|
challengeMsg.Payload, _ = core.ReadBytes(r.Len(), r)
|
|
n.challengeMessage = challengeMsg
|
|
glog.Debugf("challengeMsg:%+v", challengeMsg)
|
|
|
|
serverName := challengeMsg.getTargetName()
|
|
serverInfo := challengeMsg.getTargetInfo()
|
|
timestamp := challengeMsg.getTargetInfoTimestamp(serverInfo)
|
|
computeMIC := false
|
|
if timestamp == nil {
|
|
ft := uint64(time.Now().UnixNano()) / 100
|
|
ft += 116444736000000000 // add time between unix & windows offset
|
|
timestamp = make([]byte, 8)
|
|
binary.LittleEndian.PutUint64(timestamp, ft)
|
|
} else {
|
|
computeMIC = true
|
|
}
|
|
glog.Infof("serverName=%+v", core.UnicodeDecode(serverName))
|
|
serverChallenge := challengeMsg.ServerChallenge[:]
|
|
clientChallenge := core.Random(8)
|
|
ntChallengeResponse, lmChallengeResponse, SessionBaseKey := n.ComputeResponseV2(
|
|
n.respKeyNT, n.respKeyLM, serverChallenge, clientChallenge, timestamp, serverInfo)
|
|
|
|
exchangeKey := SessionBaseKey
|
|
exportedSessionKey := core.Random(16)
|
|
EncryptedRandomSessionKey := make([]byte, len(exportedSessionKey))
|
|
rc, _ := rc4.NewCipher(exchangeKey)
|
|
rc.XORKeyStream(EncryptedRandomSessionKey, exportedSessionKey)
|
|
|
|
if challengeMsg.NegotiateFlags&NTLMSSP_NEGOTIATE_UNICODE != 0 {
|
|
n.enableUnicode = true
|
|
}
|
|
glog.Infof("user: %s, passwd:%s", n.user, n.password)
|
|
domain, user, _ := n.GetEncodedCredentials()
|
|
|
|
n.authenticateMessage = NewAuthenticateMessage(challengeMsg.NegotiateFlags,
|
|
domain, user, []byte(""), lmChallengeResponse, ntChallengeResponse, EncryptedRandomSessionKey)
|
|
|
|
if computeMIC {
|
|
copy(n.authenticateMessage.MIC[:], MIC(exportedSessionKey, n.negotiateMessage, n.challengeMessage, n.authenticateMessage)[:16])
|
|
}
|
|
|
|
md := md5.New()
|
|
//ClientSigningKey
|
|
a := concat(exportedSessionKey, clientSigning)
|
|
md.Write(a)
|
|
ClientSigningKey := md.Sum(nil)
|
|
//ServerSigningKey
|
|
md.Reset()
|
|
a = concat(exportedSessionKey, serverSigning)
|
|
md.Write(a)
|
|
ServerSigningKey := md.Sum(nil)
|
|
//ClientSealingKey
|
|
md.Reset()
|
|
a = concat(exportedSessionKey, clientSealing)
|
|
md.Write(a)
|
|
ClientSealingKey := md.Sum(nil)
|
|
//ServerSealingKey
|
|
md.Reset()
|
|
a = concat(exportedSessionKey, serverSealing)
|
|
md.Write(a)
|
|
ServerSealingKey := md.Sum(nil)
|
|
|
|
glog.Debugf("ClientSigningKey:%s", hex.EncodeToString(ClientSigningKey))
|
|
glog.Debugf("ServerSigningKey:%s", hex.EncodeToString(ServerSigningKey))
|
|
glog.Debugf("ClientSealingKey:%s", hex.EncodeToString(ClientSealingKey))
|
|
glog.Debugf("ServerSealingKey:%s", hex.EncodeToString(ServerSealingKey))
|
|
|
|
encryptRC4, _ := rc4.NewCipher(ClientSealingKey)
|
|
decryptRC4, _ := rc4.NewCipher(ServerSealingKey)
|
|
|
|
ntlmSec := &NTLMv2Security{encryptRC4, decryptRC4, ClientSigningKey, ServerSigningKey, 0}
|
|
|
|
return n.authenticateMessage, ntlmSec
|
|
}
|
|
|
|
func (n *NTLMv2) GetEncodedCredentials() ([]byte, []byte, []byte) {
|
|
if n.enableUnicode {
|
|
return core.UnicodeEncode(n.domain), core.UnicodeEncode(n.user), core.UnicodeEncode(n.password)
|
|
}
|
|
return []byte(n.domain), []byte(n.user), []byte(n.password)
|
|
}
|
|
|
|
type NTLMv2Security struct {
|
|
EncryptRC4 *rc4.Cipher
|
|
DecryptRC4 *rc4.Cipher
|
|
SigningKey []byte
|
|
VerifyKey []byte
|
|
SeqNum uint32
|
|
}
|
|
|
|
func (n *NTLMv2Security) GssEncrypt(s []byte) []byte {
|
|
p := make([]byte, len(s))
|
|
n.EncryptRC4.XORKeyStream(p, s)
|
|
b := &bytes.Buffer{}
|
|
|
|
//signature
|
|
core.WriteUInt32LE(n.SeqNum, b)
|
|
core.WriteBytes(s, b)
|
|
s1 := HMAC_MD5(n.SigningKey, b.Bytes())[:8]
|
|
checksum := make([]byte, 8)
|
|
n.EncryptRC4.XORKeyStream(checksum, s1)
|
|
b.Reset()
|
|
core.WriteUInt32LE(0x00000001, b)
|
|
core.WriteBytes(checksum, b)
|
|
core.WriteUInt32LE(n.SeqNum, b)
|
|
|
|
core.WriteBytes(p, b)
|
|
|
|
n.SeqNum++
|
|
|
|
return b.Bytes()
|
|
}
|
|
func (n *NTLMv2Security) GssDecrypt(s []byte) []byte {
|
|
r := bytes.NewReader(s)
|
|
core.ReadUInt32LE(r) //version
|
|
checksum, _ := core.ReadBytes(8, r)
|
|
seqNum, _ := core.ReadUInt32LE(r)
|
|
data, _ := core.ReadBytes(r.Len(), r)
|
|
|
|
p := make([]byte, len(data))
|
|
n.DecryptRC4.XORKeyStream(p, data)
|
|
|
|
check := make([]byte, len(checksum))
|
|
n.DecryptRC4.XORKeyStream(check, checksum)
|
|
|
|
b := &bytes.Buffer{}
|
|
core.WriteUInt32LE(seqNum, b)
|
|
core.WriteBytes(p, b)
|
|
verify := HMAC_MD5(n.VerifyKey, b.Bytes())
|
|
if string(verify) != string(check) {
|
|
return nil
|
|
}
|
|
return p
|
|
}
|