mirror of
https://github.com/Mrs4s/MiraiGo.git
synced 2025-05-03 18:47:41 +08:00
first commit.
This commit is contained in:
commit
ddfd670dac
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
416
binary/jce/reader.go
Normal file
416
binary/jce/reader.go
Normal file
@ -0,0 +1,416 @@
|
||||
package jce
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type JceReader struct {
|
||||
buf *bytes.Reader
|
||||
data []byte
|
||||
}
|
||||
|
||||
type HeadData struct {
|
||||
Type byte
|
||||
Tag int
|
||||
}
|
||||
|
||||
func NewJceReader(data []byte) *JceReader {
|
||||
buf := bytes.NewReader(data)
|
||||
return &JceReader{buf: buf, data: data}
|
||||
}
|
||||
|
||||
func (r *JceReader) readHead() (hd *HeadData, l int32) {
|
||||
hd = &HeadData{}
|
||||
b, _ := r.buf.ReadByte()
|
||||
hd.Type = b & 0xF
|
||||
hd.Tag = (int(b) & 0xF0) >> 4
|
||||
if hd.Tag == 15 {
|
||||
b, _ = r.buf.ReadByte()
|
||||
hd.Tag = int(b) & 0xFF
|
||||
return hd, 2
|
||||
}
|
||||
return hd, 1
|
||||
}
|
||||
|
||||
func (r *JceReader) peakHead() (hd *HeadData, l int32) {
|
||||
offset := r.buf.Size() - int64(r.buf.Len())
|
||||
n := NewJceReader(r.data[offset:])
|
||||
return n.readHead()
|
||||
}
|
||||
|
||||
func (r *JceReader) skip(l int) {
|
||||
r.readBytes(l)
|
||||
}
|
||||
|
||||
func (r *JceReader) skipField(t byte) {
|
||||
switch t {
|
||||
case 0:
|
||||
r.skip(1)
|
||||
case 1:
|
||||
r.skip(2)
|
||||
case 2, 4:
|
||||
r.skip(4)
|
||||
case 3, 5:
|
||||
r.skip(8)
|
||||
case 6:
|
||||
b, _ := r.buf.ReadByte()
|
||||
r.skip(int(b))
|
||||
case 7:
|
||||
r.skip(int(r.readInt32()))
|
||||
case 8:
|
||||
s := r.ReadInt32(0)
|
||||
for i := 0; i < int(s)*2; i++ {
|
||||
r.skipNextField()
|
||||
}
|
||||
case 9:
|
||||
s := r.ReadInt32(0)
|
||||
for i := 0; i < int(s); i++ {
|
||||
r.skipNextField()
|
||||
}
|
||||
case 13:
|
||||
r.readHead()
|
||||
s := r.ReadInt32(0)
|
||||
r.skip(int(s))
|
||||
case 10:
|
||||
r.skipToStructEnd()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *JceReader) skipNextField() {
|
||||
hd, _ := r.readHead()
|
||||
r.skipField(hd.Type)
|
||||
}
|
||||
|
||||
func (r *JceReader) SkipField(c int) {
|
||||
for i := 0; i < c; i++ {
|
||||
r.skipNextField()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *JceReader) readBytes(len int) []byte {
|
||||
b := make([]byte, len)
|
||||
_, err := r.buf.Read(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (r *JceReader) readByte() byte {
|
||||
return r.readBytes(1)[0]
|
||||
}
|
||||
|
||||
func (r *JceReader) readUInt16() uint16 {
|
||||
f, _ := r.buf.ReadByte()
|
||||
s, err := r.buf.ReadByte()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return uint16((int32(f) << 8) + int32(s))
|
||||
}
|
||||
|
||||
func (r *JceReader) readInt32() int32 {
|
||||
b := r.readBytes(4)
|
||||
return (int32(b[0]) << 24) | (int32(b[1]) << 16) | (int32(b[2]) << 8) | int32(b[3])
|
||||
}
|
||||
|
||||
func (r *JceReader) readInt64() int64 {
|
||||
b := r.readBytes(8)
|
||||
return (int64(b[0]) << 56) | (int64(b[1]) << 48) | (int64(b[2]) << 40) | (int64(b[3]) << 32) | (int64(b[4]) << 24) | (int64(b[5]) << 16) | (int64(b[6]) << 8) | int64(b[7])
|
||||
}
|
||||
|
||||
func (r *JceReader) readFloat32() float32 {
|
||||
b := r.readInt32()
|
||||
return math.Float32frombits(uint32(b))
|
||||
}
|
||||
|
||||
func (r *JceReader) readFloat64() float64 {
|
||||
b := r.readInt64()
|
||||
return math.Float64frombits(uint64(b))
|
||||
}
|
||||
|
||||
func (r *JceReader) skipToTag(tag int) bool {
|
||||
for {
|
||||
hd, l := r.peakHead()
|
||||
if tag <= hd.Tag || hd.Type == 11 {
|
||||
return tag == hd.Tag
|
||||
}
|
||||
r.skip(int(l))
|
||||
r.skipField(hd.Type)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *JceReader) skipToStructEnd() {
|
||||
for {
|
||||
hd, _ := r.readHead()
|
||||
r.skipField(hd.Type)
|
||||
if hd.Type == 11 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *JceReader) ReadByte(tag int) byte {
|
||||
if !r.skipToTag(tag) {
|
||||
return 0
|
||||
}
|
||||
hd, _ := r.readHead()
|
||||
switch hd.Type {
|
||||
case 12:
|
||||
return 0
|
||||
case 0:
|
||||
return r.readByte()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (r *JceReader) ReadBool(tag int) bool {
|
||||
return r.ReadByte(tag) != 0
|
||||
}
|
||||
|
||||
func (r *JceReader) ReadInt16(tag int) int16 {
|
||||
if !r.skipToTag(tag) {
|
||||
return 0
|
||||
}
|
||||
hd, _ := r.readHead()
|
||||
switch hd.Type {
|
||||
case 12:
|
||||
return 0
|
||||
case 0:
|
||||
return int16(r.readByte())
|
||||
case 1:
|
||||
return int16(r.readUInt16())
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (r *JceReader) ReadInt32(tag int) int32 {
|
||||
if !r.skipToTag(tag) {
|
||||
return 0
|
||||
}
|
||||
hd, _ := r.readHead()
|
||||
switch hd.Type {
|
||||
case 12:
|
||||
return 0
|
||||
case 0:
|
||||
return int32(r.readByte())
|
||||
case 1:
|
||||
return int32(r.readUInt16())
|
||||
case 2:
|
||||
return r.readInt32()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (r *JceReader) ReadInt64(tag int) int64 {
|
||||
if !r.skipToTag(tag) {
|
||||
return 0
|
||||
}
|
||||
hd, _ := r.readHead()
|
||||
switch hd.Type {
|
||||
case 12:
|
||||
return 0
|
||||
case 0:
|
||||
return int64(r.readByte())
|
||||
case 1:
|
||||
return int64(int16(r.readUInt16()))
|
||||
case 2:
|
||||
return int64(r.readInt32())
|
||||
case 3:
|
||||
return r.readInt64()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (r *JceReader) ReadFloat32(tag int) float32 {
|
||||
if !r.skipToTag(tag) {
|
||||
return 0
|
||||
}
|
||||
hd, _ := r.readHead()
|
||||
switch hd.Type {
|
||||
case 12:
|
||||
return 0
|
||||
case 4:
|
||||
return r.readFloat32()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (r *JceReader) ReadFloat64(tag int) float64 {
|
||||
if !r.skipToTag(tag) {
|
||||
return 0
|
||||
}
|
||||
hd, _ := r.readHead()
|
||||
switch hd.Type {
|
||||
case 12:
|
||||
return 0
|
||||
case 4:
|
||||
return float64(r.readFloat32())
|
||||
case 5:
|
||||
return r.readFloat64()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (r *JceReader) ReadString(tag int) string {
|
||||
if !r.skipToTag(tag) {
|
||||
return ""
|
||||
}
|
||||
hd, _ := r.readHead()
|
||||
switch hd.Type {
|
||||
case 6:
|
||||
return string(r.readBytes(int(r.readByte())))
|
||||
case 7:
|
||||
return string(r.readBytes(int(r.readInt32())))
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// ReadAny Read any type via tag, unsupported JceStruct
|
||||
func (r *JceReader) ReadAny(tag int) interface{} {
|
||||
if !r.skipToTag(tag) {
|
||||
return nil
|
||||
}
|
||||
hd, _ := r.readHead()
|
||||
switch hd.Type {
|
||||
case 0:
|
||||
return r.readByte()
|
||||
case 1:
|
||||
return r.readUInt16()
|
||||
case 2:
|
||||
return r.readInt32()
|
||||
case 3:
|
||||
return r.readInt64()
|
||||
case 4:
|
||||
return r.readFloat32()
|
||||
case 5:
|
||||
return r.readFloat64()
|
||||
case 6:
|
||||
return string(r.readBytes(int(r.readByte())))
|
||||
case 7:
|
||||
return string(r.readBytes(int(r.readInt32())))
|
||||
case 8:
|
||||
s := r.ReadInt32(0)
|
||||
m := make(map[interface{}]interface{})
|
||||
for i := 0; i < int(s); i++ {
|
||||
m[r.ReadAny(0)] = r.ReadAny(1)
|
||||
}
|
||||
return m
|
||||
case 9:
|
||||
var sl []interface{}
|
||||
s := r.ReadInt32(0)
|
||||
for i := 0; i < int(s); i++ {
|
||||
sl = append(sl, r.ReadAny(0))
|
||||
}
|
||||
return sl
|
||||
case 12:
|
||||
return 0
|
||||
case 13:
|
||||
r.readHead()
|
||||
return r.readBytes(int(r.ReadInt32(0)))
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r *JceReader) ReadMapF(tag int, f func(interface{}, interface{})) {
|
||||
if !r.skipToTag(tag) {
|
||||
return
|
||||
}
|
||||
r.readHead()
|
||||
s := r.ReadInt32(0)
|
||||
for i := 0; i < int(s); i++ {
|
||||
k := r.ReadAny(0)
|
||||
v := r.ReadAny(1)
|
||||
if k != nil {
|
||||
f(k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *JceReader) readObject(t reflect.Type, tag int) reflect.Value {
|
||||
switch t.Kind() {
|
||||
case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
var i int64
|
||||
r.ReadObject(&i, tag)
|
||||
return reflect.ValueOf(i)
|
||||
case reflect.String:
|
||||
var s string
|
||||
r.ReadObject(&s, tag)
|
||||
return reflect.ValueOf(s)
|
||||
case reflect.Slice:
|
||||
s := reflect.New(t.Elem()).Interface().(IJecStruct)
|
||||
r.readHead()
|
||||
s.ReadFrom(r)
|
||||
r.skipToStructEnd()
|
||||
return reflect.ValueOf(s).Elem()
|
||||
}
|
||||
return reflect.ValueOf(nil)
|
||||
}
|
||||
|
||||
func (r *JceReader) ReadSlice(i interface{}, tag int) {
|
||||
t := reflect.TypeOf(i)
|
||||
v := reflect.ValueOf(i).Elem()
|
||||
if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Slice {
|
||||
return
|
||||
}
|
||||
if v.IsNil() {
|
||||
return
|
||||
}
|
||||
if !r.skipToTag(tag) {
|
||||
return
|
||||
}
|
||||
hd, _ := r.readHead()
|
||||
if hd.Type == 9 {
|
||||
s := r.ReadInt32(0)
|
||||
for i := 0; i < int(s); i++ {
|
||||
val := r.readObject(t.Elem(), 0)
|
||||
v.Set(reflect.Append(v, val))
|
||||
}
|
||||
}
|
||||
if hd.Type == 13 {
|
||||
r.readHead()
|
||||
arr := r.readBytes(int(r.ReadInt32(0)))
|
||||
for _, b := range arr {
|
||||
v.Set(reflect.Append(v, reflect.ValueOf(b)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *JceReader) ReadObject(i interface{}, tag int) {
|
||||
va := reflect.ValueOf(i)
|
||||
if va.Kind() != reflect.Ptr || va.IsNil() {
|
||||
return
|
||||
}
|
||||
switch o := i.(type) {
|
||||
case *byte:
|
||||
*o = r.ReadByte(tag)
|
||||
case *bool:
|
||||
*o = r.ReadBool(tag)
|
||||
case *int16:
|
||||
*o = r.ReadInt16(tag)
|
||||
case *int:
|
||||
*o = int(r.ReadInt32(tag))
|
||||
case *int32:
|
||||
*o = r.ReadInt32(tag)
|
||||
case *int64:
|
||||
*o = r.ReadInt64(tag)
|
||||
case *float32:
|
||||
*o = r.ReadFloat32(tag)
|
||||
case *float64:
|
||||
*o = r.ReadFloat64(tag)
|
||||
case *string:
|
||||
*o = r.ReadString(tag)
|
||||
case IJecStruct:
|
||||
o.ReadFrom(r)
|
||||
}
|
||||
}
|
397
binary/jce/structs.go
Normal file
397
binary/jce/structs.go
Normal file
@ -0,0 +1,397 @@
|
||||
package jce
|
||||
|
||||
type IJecStruct interface {
|
||||
ToBytes() []byte
|
||||
ReadFrom(*JceReader)
|
||||
}
|
||||
|
||||
// TODO: code gen
|
||||
type (
|
||||
RequestPacket struct {
|
||||
IVersion int16 `jceId:"1"`
|
||||
CPacketType byte `jceId:"2"`
|
||||
IMessageType int32 `jceId:"3"`
|
||||
IRequestId int32 `jceId:"4"`
|
||||
SServantName string `jceId:"5"`
|
||||
SFuncName string `jceId:"6"`
|
||||
SBuffer []byte `jceId:"7"`
|
||||
ITimeout int32 `jceId:"8"`
|
||||
Context map[string]string `jceId:"9"`
|
||||
Status map[string]string `jceId:"10"`
|
||||
}
|
||||
|
||||
RequestDataVersion3 struct {
|
||||
Map map[string][]byte `jceId:"0"`
|
||||
}
|
||||
|
||||
RequestDataVersion2 struct {
|
||||
Map map[string]map[string][]byte `jceId:"0"`
|
||||
}
|
||||
|
||||
SvcReqRegister struct {
|
||||
Uin int64 `jceId:"0"`
|
||||
Bid int64 `jceId:"1"`
|
||||
ConnType byte `jceId:"2"`
|
||||
Other string `jceId:"3"`
|
||||
Status int32 `jceId:"4"`
|
||||
OnlinePush byte `jceId:"5"`
|
||||
IsOnline byte `jceId:"6"`
|
||||
IsShowOnline byte `jceId:"7"`
|
||||
KickPC byte `jceId:"8"`
|
||||
KickWeak byte `jceId:"9"`
|
||||
Timestamp int64 `jceId:"10"`
|
||||
IOSVersion int64 `jceId:"11"`
|
||||
NetType byte `jceId:"12"`
|
||||
BuildVer string `jceId:"13"`
|
||||
RegType byte `jceId:"14"`
|
||||
DevParam []byte `jceId:"15"`
|
||||
Guid []byte `jceId:"16"`
|
||||
LocaleId int32 `jceId:"17"`
|
||||
SilentPush byte `jceId:"18"`
|
||||
DevName string `jceId:"19"`
|
||||
DevType string `jceId:"20"`
|
||||
OSVer string `jceId:"21"`
|
||||
OpenPush byte `jceId:"22"`
|
||||
LargeSeq int64 `jceId:"23"`
|
||||
LastWatchStartTime int64 `jceId:"24"`
|
||||
OldSSOIp int64 `jceId:"26"`
|
||||
NewSSOIp int64 `jceId:"27"`
|
||||
ChannelNo string `jceId:"28"`
|
||||
CPID int64 `jceId:"29"`
|
||||
VendorName string `jceId:"30"`
|
||||
VendorOSName string `jceId:"31"`
|
||||
IOSIdfa string `jceId:"32"`
|
||||
B769 []byte `jceId:"33"`
|
||||
IsSetStatus byte `jceId:"34"`
|
||||
ServerBuf []byte `jceId:"35"`
|
||||
SetMute byte `jceId:"36"`
|
||||
}
|
||||
|
||||
FriendListRequest struct {
|
||||
Reqtype int32 `jceId:"0"`
|
||||
IfReflush byte `jceId:"1"`
|
||||
Uin int64 `jceId:"2"`
|
||||
StartIndex int16 `jceId:"3"`
|
||||
FriendCount int16 `jceId:"4"`
|
||||
GroupId byte `jceId:"5"`
|
||||
IfGetGroupInfo byte `jceId:"6"`
|
||||
GroupStartIndex byte `jceId:"7"`
|
||||
GroupCount byte `jceId:"8"`
|
||||
IfGetMSFGroup byte `jceId:"9"`
|
||||
IfShowTermType byte `jceId:"10"`
|
||||
Version int64 `jceId:"11"`
|
||||
UinList []int64 `jceId:"12"`
|
||||
AppType int32 `jceId:"13"`
|
||||
IfGetDOVId byte `jceId:"14"`
|
||||
IfGetBothFlag byte `jceId:"15"`
|
||||
D50 []byte `jceId:"16"`
|
||||
D6B []byte `jceId:"17"`
|
||||
SnsTypeList []int64 `jceId:"18"`
|
||||
}
|
||||
|
||||
FriendInfo struct {
|
||||
FriendUin int64 `jceId:"0"`
|
||||
GroupId byte `jceId:"1"`
|
||||
FaceId int16 `jceId:"2"`
|
||||
Remark string `jceId:"3"`
|
||||
QQType byte `jceId:"4"`
|
||||
Status byte `jceId:"5"`
|
||||
MemberLevel byte `jceId:"6"`
|
||||
IsMqqOnLine byte `jceId:"7"`
|
||||
QQOnlineState byte `jceId:"8"`
|
||||
IsIphoneOnline byte `jceId:"9"`
|
||||
DetailStatusFlag byte `jceId:"10"`
|
||||
QQOnlineStateV2 byte `jceId:"11"`
|
||||
ShowName string `jceId:"12"`
|
||||
IsRemark byte `jceId:"13"`
|
||||
Nick string `jceId:"14"`
|
||||
SpecialFlag byte `jceId:"15"`
|
||||
IMGroupID []byte `jceId:"16"`
|
||||
MSFGroupID []byte `jceId:"17"`
|
||||
TermType int32 `jceId:"18"`
|
||||
Network byte `jceId:"20"`
|
||||
Ring []byte `jceId:"21"`
|
||||
AbiFlag int64 `jceId:"22"`
|
||||
FaceAddonId int64 `jceId:"23"`
|
||||
NetworkType int32 `jceId:"24"`
|
||||
VipFont int64 `jceId:"25"`
|
||||
IconType int32 `jceId:"26"`
|
||||
TermDesc string `jceId:"27"`
|
||||
ColorRing int64 `jceId:"28"`
|
||||
ApolloFlag byte `jceId:"29"`
|
||||
ApolloTimestamp int64 `jceId:"30"`
|
||||
Sex byte `jceId:"31"`
|
||||
FounderFont int64 `jceId:"32"`
|
||||
EimId string `jceId:"33"`
|
||||
EimMobile string `jceId:"34"`
|
||||
OlympicTorch byte `jceId:"35"`
|
||||
ApolloSignTime int64 `jceId:"36"`
|
||||
LaviUin int64 `jceId:"37"`
|
||||
TagUpdateTime int64 `jceId:"38"`
|
||||
GameLastLoginTime int64 `jceId:"39"`
|
||||
GameAppId int64 `jceId:"40"`
|
||||
CardID []byte `jceId:"41"`
|
||||
BitSet int64 `jceId:"42"`
|
||||
KingOfGloryFlag byte `jceId:"43"`
|
||||
KingOfGloryRank int64 `jceId:"44"`
|
||||
MasterUin string `jceId:"45"`
|
||||
LastMedalUpdateTime int64 `jceId:"46"`
|
||||
FaceStoreId int64 `jceId:"47"`
|
||||
FontEffect int64 `jceId:"48"`
|
||||
DOVId string `jceId:"49"`
|
||||
BothFlag int64 `jceId:"50"`
|
||||
CentiShow3DFlag byte `jceId:"51"`
|
||||
IntimateInfo []byte `jceId:"52"`
|
||||
ShowNameplate byte `jceId:"53"`
|
||||
NewLoverDiamondFlag byte `jceId:"54"`
|
||||
ExtSnsFrdData []byte `jceId:"55"`
|
||||
MutualMarkData []byte `jceId:"56"`
|
||||
}
|
||||
|
||||
TroopListRequest struct {
|
||||
Uin int64 `jceId:"0"`
|
||||
GetMSFMsgFlag byte `jceId:"1"`
|
||||
Cookies []byte `jceId:"2"`
|
||||
GroupInfo []int64 `jceId:"3"`
|
||||
GroupFlagExt byte `jceId:"4"`
|
||||
Version int32 `jceId:"5"`
|
||||
CompanyId int64 `jceId:"6"`
|
||||
VersionNum int64 `jceId:"7"`
|
||||
GetLongGroupName byte `jceId:"8"`
|
||||
}
|
||||
|
||||
TroopNumber struct {
|
||||
GroupUin int64 `jceId:"0"`
|
||||
GroupCode int64 `jceId:"1"`
|
||||
Flag byte `jceId:"2"`
|
||||
GroupInfoSeq int64 `jceId:"3"`
|
||||
GroupName string `jceId:"4"`
|
||||
GroupMemo string `jceId:"5"`
|
||||
GroupFlagExt int64 `jceId:"6"`
|
||||
GroupRankSeq int64 `jceId:"7"`
|
||||
CertificationType int64 `jceId:"8"`
|
||||
ShutUpTimestamp int64 `jceId:"9"`
|
||||
MyShutUpTimestamp int64 `jceId:"10"`
|
||||
CmdUinUinFlag int64 `jceId:"11"`
|
||||
AdditionalFlag int64 `jceId:"12"`
|
||||
GroupTypeFlag int64 `jceId:"13"`
|
||||
GroupSecType int64 `jceId:"14"`
|
||||
GroupSecTypeInfo int64 `jceId:"15"`
|
||||
GroupClassExt int64 `jceId:"16"`
|
||||
AppPrivilegeFlag int64 `jceId:"17"`
|
||||
SubscriptionUin int64 `jceId:"18"`
|
||||
MemberNum int64 `jceId:"19"`
|
||||
MemberNumSeq int64 `jceId:"20"`
|
||||
MemberCardSeq int64 `jceId:"21"`
|
||||
GroupFlagExt3 int64 `jceId:"22"`
|
||||
GroupOwnerUin int64 `jceId:"23"`
|
||||
IsConfGroup byte `jceId:"24"`
|
||||
IsModifyConfGroupFace byte `jceId:"25"`
|
||||
IsModifyConfGroupName byte `jceId:"26"`
|
||||
CmdUinJoinTime int64 `jceId:"27"`
|
||||
CompanyId int64 `jceId:"28"`
|
||||
MaxGroupMemberNum int64 `jceId:"29"`
|
||||
CmdUinGroupMask int64 `jceId:"30"`
|
||||
GuildAppId int64 `jceId:"31"`
|
||||
GuildSubType int64 `jceId:"32"`
|
||||
CmdUinRingtoneID int64 `jceId:"33"`
|
||||
CmdUinFlagEx2 int64 `jceId:"34"`
|
||||
}
|
||||
|
||||
TroopMemberListRequest struct {
|
||||
Uin int64 `jceId:"0"`
|
||||
GroupCode int64 `jceId:"1"`
|
||||
NextUin int64 `jceId:"2"`
|
||||
GroupUin int64 `jceId:"3"`
|
||||
Version int64 `jceId:"4"`
|
||||
ReqType int64 `jceId:"5"`
|
||||
GetListAppointTime int64 `jceId:"6"`
|
||||
RichCardNameVer byte `jceId:"7"`
|
||||
}
|
||||
|
||||
TroopMemberInfo struct {
|
||||
MemberUin int64 `jceId:"0"`
|
||||
FaceId int16 `jceId:"1"`
|
||||
Age byte `jceId:"2"`
|
||||
Gender byte `jceId:"3"`
|
||||
Nick string `jceId:"4"`
|
||||
Status byte `jceId:"5"`
|
||||
ShowName string `jceId:"6"`
|
||||
Name string `jceId:"8"`
|
||||
Memo string `jceId:"12"`
|
||||
AutoRemark string `jceId:"13"`
|
||||
MemberLevel int64 `jceId:"14"`
|
||||
JoinTime int64 `jceId:"15"`
|
||||
LastSpeakTime int64 `jceId:"16"`
|
||||
CreditLevel int64 `jceId:"17"`
|
||||
Flag int64 `jceId:"18"`
|
||||
FlagExt int64 `jceId:"19"`
|
||||
Point int64 `jceId:"20"`
|
||||
Concerned byte `jceId:"21"`
|
||||
Shielded byte `jceId:"22"`
|
||||
SpecialTitle string `jceId:"23"`
|
||||
SpecialTitleExpireTime int64 `jceId:"24"`
|
||||
Job string `jceId:"25"`
|
||||
ApolloFlag byte `jceId:"26"`
|
||||
ApolloTimestamp int64 `jceId:"27"`
|
||||
GlobalGroupLevel int64 `jceId:"28"`
|
||||
TitleId int64 `jceId:"29"`
|
||||
ShutUpTimestap int64 `jceId:"30"`
|
||||
GlobalGroupPoint int64 `jceId:"31"`
|
||||
RichCardNameVer byte `jceId:"33"`
|
||||
VipType int64 `jceId:"34"`
|
||||
VipLevel int64 `jceId:"35"`
|
||||
BigClubLevel int64 `jceId:"36"`
|
||||
BigClubFlag int64 `jceId:"37"`
|
||||
Nameplate int64 `jceId:"38"`
|
||||
GroupHonor []byte `jceId:"39"`
|
||||
}
|
||||
)
|
||||
|
||||
func (pkt *RequestPacket) ToBytes() []byte {
|
||||
w := NewJceWriter()
|
||||
w.WriteJceStructRaw(pkt)
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
func (pkt *RequestPacket) ReadFrom(r *JceReader) {
|
||||
pkt.SBuffer = []byte{}
|
||||
pkt.Context = make(map[string]string)
|
||||
pkt.Status = make(map[string]string)
|
||||
pkt.IVersion = r.ReadInt16(1)
|
||||
pkt.CPacketType = r.ReadByte(2)
|
||||
pkt.IMessageType = r.ReadInt32(3)
|
||||
pkt.IRequestId = r.ReadInt32(4)
|
||||
pkt.SServantName = r.ReadString(5)
|
||||
pkt.SFuncName = r.ReadString(6)
|
||||
r.ReadSlice(&pkt.SBuffer, 7)
|
||||
pkt.ITimeout = r.ReadInt32(8)
|
||||
r.ReadMapF(9, func(k interface{}, v interface{}) { pkt.Context[k.(string)] = v.(string) })
|
||||
r.ReadMapF(10, func(k interface{}, v interface{}) { pkt.Status[k.(string)] = v.(string) })
|
||||
}
|
||||
|
||||
func (pkt *RequestDataVersion3) ToBytes() []byte {
|
||||
w := NewJceWriter()
|
||||
w.WriteJceStructRaw(pkt)
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
func (pkt *RequestDataVersion3) ReadFrom(r *JceReader) {
|
||||
pkt.Map = make(map[string][]byte)
|
||||
r.ReadMapF(0, func(k interface{}, v interface{}) {
|
||||
pkt.Map[k.(string)] = v.([]byte)
|
||||
})
|
||||
}
|
||||
|
||||
func (pkt *RequestDataVersion2) ToBytes() []byte {
|
||||
w := NewJceWriter()
|
||||
w.WriteJceStructRaw(pkt)
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
func (pkt *RequestDataVersion2) ReadFrom(r *JceReader) {
|
||||
pkt.Map = make(map[string]map[string][]byte)
|
||||
r.ReadMapF(0, func(k interface{}, v interface{}) {
|
||||
pkt.Map[k.(string)] = make(map[string][]byte)
|
||||
for k2, v := range v.(map[interface{}]interface{}) {
|
||||
pkt.Map[k.(string)][k2.(string)] = v.([]byte)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (pkt *SvcReqRegister) ToBytes() []byte {
|
||||
w := NewJceWriter()
|
||||
w.WriteJceStructRaw(pkt)
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
func (pkt *SvcReqRegister) ReadFrom(r *JceReader) {
|
||||
|
||||
}
|
||||
|
||||
func (pkt *FriendListRequest) ToBytes() []byte {
|
||||
w := NewJceWriter()
|
||||
w.WriteJceStructRaw(pkt)
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
func (pkt *FriendListRequest) ReadFrom(r *JceReader) {
|
||||
|
||||
}
|
||||
|
||||
func (pkt *FriendInfo) ToBytes() []byte {
|
||||
w := NewJceWriter()
|
||||
w.WriteJceStructRaw(pkt)
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
func (pkt *FriendInfo) ReadFrom(r *JceReader) {
|
||||
pkt.FriendUin = r.ReadInt64(0)
|
||||
pkt.GroupId = r.ReadByte(1)
|
||||
pkt.FaceId = r.ReadInt16(2)
|
||||
pkt.Remark = r.ReadString(3)
|
||||
pkt.Status = r.ReadByte(5)
|
||||
pkt.MemberLevel = r.ReadByte(6)
|
||||
pkt.Nick = r.ReadString(14)
|
||||
pkt.Network = r.ReadByte(20)
|
||||
pkt.NetworkType = r.ReadInt32(24)
|
||||
pkt.CardID = []byte{}
|
||||
r.ReadObject(&pkt.CardID, 41)
|
||||
}
|
||||
|
||||
func (pkt *TroopListRequest) ToBytes() []byte {
|
||||
w := NewJceWriter()
|
||||
w.WriteJceStructRaw(pkt)
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
func (pkt *TroopListRequest) ReadFrom(r *JceReader) {
|
||||
|
||||
}
|
||||
|
||||
func (pkt *TroopNumber) ToBytes() []byte {
|
||||
w := NewJceWriter()
|
||||
w.WriteJceStructRaw(pkt)
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
func (pkt *TroopNumber) ReadFrom(r *JceReader) {
|
||||
pkt.GroupUin = r.ReadInt64(0)
|
||||
pkt.GroupCode = r.ReadInt64(1)
|
||||
pkt.GroupName = r.ReadString(4)
|
||||
pkt.GroupMemo = r.ReadString(5)
|
||||
pkt.MemberNum = r.ReadInt64(19)
|
||||
pkt.GroupOwnerUin = r.ReadInt64(23)
|
||||
pkt.MaxGroupMemberNum = r.ReadInt64(29)
|
||||
}
|
||||
|
||||
func (pkt *TroopMemberListRequest) ToBytes() []byte {
|
||||
w := NewJceWriter()
|
||||
w.WriteJceStructRaw(pkt)
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
func (pkt *TroopMemberListRequest) ReadFrom(r *JceReader) {
|
||||
|
||||
}
|
||||
|
||||
func (pkt *TroopMemberInfo) ToBytes() []byte {
|
||||
w := NewJceWriter()
|
||||
w.WriteJceStructRaw(pkt)
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
func (pkt *TroopMemberInfo) ReadFrom(r *JceReader) {
|
||||
pkt.MemberUin = r.ReadInt64(0)
|
||||
pkt.FaceId = r.ReadInt16(1)
|
||||
pkt.Nick = r.ReadString(4)
|
||||
pkt.ShowName = r.ReadString(6)
|
||||
pkt.Name = r.ReadString(8)
|
||||
pkt.AutoRemark = r.ReadString(13)
|
||||
pkt.MemberLevel = r.ReadInt64(14)
|
||||
pkt.JoinTime = r.ReadInt64(15)
|
||||
pkt.LastSpeakTime = r.ReadInt64(16)
|
||||
pkt.SpecialTitle = r.ReadString(23)
|
||||
pkt.SpecialTitleExpireTime = r.ReadInt64(24)
|
||||
pkt.Job = r.ReadString(25)
|
||||
}
|
192
binary/jce/writer.go
Normal file
192
binary/jce/writer.go
Normal file
@ -0,0 +1,192 @@
|
||||
package jce
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
goBinary "encoding/binary"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type JceWriter struct {
|
||||
buf *bytes.Buffer
|
||||
}
|
||||
|
||||
func NewJceWriter() *JceWriter {
|
||||
return &JceWriter{buf: new(bytes.Buffer)}
|
||||
}
|
||||
|
||||
func (w *JceWriter) writeHead(t byte, tag int) {
|
||||
if tag < 15 {
|
||||
b := byte(tag<<4) | t
|
||||
w.buf.WriteByte(b)
|
||||
} else if tag < 256 {
|
||||
b := 0xF0 | t
|
||||
w.buf.WriteByte(b)
|
||||
w.buf.WriteByte(byte(tag))
|
||||
}
|
||||
}
|
||||
|
||||
func (w *JceWriter) WriteByte(b byte, tag int) {
|
||||
if b == 0 {
|
||||
w.writeHead(12, tag)
|
||||
} else {
|
||||
w.writeHead(0, tag)
|
||||
w.buf.WriteByte(b)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *JceWriter) WriteBool(b bool, tag int) {
|
||||
var by byte = 0
|
||||
if b {
|
||||
by = 1
|
||||
}
|
||||
w.WriteByte(by, tag)
|
||||
}
|
||||
|
||||
func (w *JceWriter) WriteInt16(n int16, tag int) {
|
||||
if n >= -128 && n <= 127 {
|
||||
w.WriteByte(byte(n), tag)
|
||||
return
|
||||
}
|
||||
w.writeHead(1, tag)
|
||||
_ = goBinary.Write(w.buf, goBinary.BigEndian, n)
|
||||
}
|
||||
|
||||
func (w *JceWriter) WriteInt32(n int32, tag int) {
|
||||
if n >= -32768 && n <= 32767 { // ? if ((n >= 32768) && (n <= 32767))
|
||||
w.WriteInt16(int16(n), tag)
|
||||
return
|
||||
}
|
||||
w.writeHead(2, tag)
|
||||
_ = goBinary.Write(w.buf, goBinary.BigEndian, n)
|
||||
}
|
||||
|
||||
func (w *JceWriter) WriteInt64(n int64, tag int) {
|
||||
if n >= -2147483648 && n <= 2147483647 {
|
||||
w.WriteInt32(int32(n), tag)
|
||||
return
|
||||
}
|
||||
w.writeHead(3, tag)
|
||||
_ = goBinary.Write(w.buf, goBinary.BigEndian, n)
|
||||
}
|
||||
|
||||
func (w *JceWriter) WriteFloat32(n float32, tag int) {
|
||||
w.writeHead(4, tag)
|
||||
_ = goBinary.Write(w.buf, goBinary.BigEndian, n)
|
||||
}
|
||||
|
||||
func (w *JceWriter) WriteFloat64(n float64, tag int) {
|
||||
w.writeHead(5, tag)
|
||||
_ = goBinary.Write(w.buf, goBinary.BigEndian, n)
|
||||
}
|
||||
|
||||
func (w *JceWriter) WriteString(s string, tag int) {
|
||||
by := []byte(s)
|
||||
if len(by) > 255 {
|
||||
w.writeHead(7, tag)
|
||||
_ = goBinary.Write(w.buf, goBinary.BigEndian, len(by))
|
||||
w.buf.Write(by)
|
||||
return
|
||||
}
|
||||
w.writeHead(6, tag)
|
||||
w.buf.WriteByte(byte(len(by)))
|
||||
w.buf.Write(by)
|
||||
}
|
||||
|
||||
func (w *JceWriter) WriteBytes(l []byte, tag int) {
|
||||
w.writeHead(13, tag)
|
||||
w.writeHead(0, 0)
|
||||
w.WriteInt32(int32(len(l)), 0)
|
||||
w.buf.Write(l)
|
||||
}
|
||||
|
||||
func (w *JceWriter) WriteInt64Slice(l []int64, tag int) {
|
||||
w.writeHead(9, tag)
|
||||
if len(l) == 0 {
|
||||
w.WriteInt32(0, 0)
|
||||
return
|
||||
}
|
||||
w.WriteInt32(int32(len(l)), 0)
|
||||
for _, v := range l {
|
||||
w.WriteInt64(v, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *JceWriter) WriteMap(m interface{}, tag int) {
|
||||
if m == nil {
|
||||
w.writeHead(8, tag)
|
||||
w.WriteInt32(0, 0)
|
||||
return
|
||||
}
|
||||
va := reflect.ValueOf(m)
|
||||
if va.Kind() != reflect.Map {
|
||||
return
|
||||
}
|
||||
w.writeHead(8, tag)
|
||||
w.WriteInt32(int32(len(va.MapKeys())), 0)
|
||||
for _, k := range va.MapKeys() {
|
||||
v := va.MapIndex(k)
|
||||
w.WriteObject(k.Interface(), 0)
|
||||
w.WriteObject(v.Interface(), 1)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *JceWriter) WriteObject(i interface{}, tag int) {
|
||||
t := reflect.TypeOf(i)
|
||||
if t.Kind() == reflect.Map {
|
||||
w.WriteMap(i, tag)
|
||||
return
|
||||
}
|
||||
switch o := i.(type) {
|
||||
case byte:
|
||||
w.WriteByte(o, tag)
|
||||
case bool:
|
||||
w.WriteBool(o, tag)
|
||||
case int16:
|
||||
w.WriteInt16(o, tag)
|
||||
case int32:
|
||||
w.WriteInt32(o, tag)
|
||||
case int64:
|
||||
w.WriteInt64(o, tag)
|
||||
case float32:
|
||||
w.WriteFloat32(o, tag)
|
||||
case float64:
|
||||
w.WriteFloat64(o, tag)
|
||||
case string:
|
||||
w.WriteString(o, tag)
|
||||
case []byte:
|
||||
w.WriteBytes(o, tag)
|
||||
case []int64:
|
||||
w.WriteInt64Slice(o, tag)
|
||||
case IJecStruct:
|
||||
w.WriteJceStruct(o, tag)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *JceWriter) WriteJceStructRaw(s IJecStruct) {
|
||||
var (
|
||||
t = reflect.TypeOf(s).Elem()
|
||||
v = reflect.ValueOf(s).Elem()
|
||||
)
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
strId := t.Field(i).Tag.Get("jceId")
|
||||
if strId == "" {
|
||||
continue
|
||||
}
|
||||
id, err := strconv.Atoi(strId)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
w.WriteObject(v.Field(i).Interface(), id)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *JceWriter) WriteJceStruct(s IJecStruct, tag int) {
|
||||
w.writeHead(10, tag)
|
||||
w.WriteJceStructRaw(s)
|
||||
w.writeHead(11, 0)
|
||||
}
|
||||
|
||||
func (w *JceWriter) Bytes() []byte {
|
||||
return w.buf.Bytes()
|
||||
}
|
146
binary/reader.go
Normal file
146
binary/reader.go
Normal file
@ -0,0 +1,146 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
)
|
||||
|
||||
type Reader struct {
|
||||
buf *bytes.Reader
|
||||
}
|
||||
|
||||
type NetworkReader struct {
|
||||
conn net.Conn
|
||||
}
|
||||
|
||||
type TlvMap map[uint16][]byte
|
||||
|
||||
// --- ByteStream reader ---
|
||||
|
||||
func NewReader(data []byte) *Reader {
|
||||
buf := bytes.NewReader(data)
|
||||
return &Reader{
|
||||
buf: buf,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reader) ReadByte() byte {
|
||||
b, err := r.buf.ReadByte()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (r *Reader) ReadBytes(len int) []byte {
|
||||
b := make([]byte, len)
|
||||
_, err := r.buf.Read(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (r *Reader) ReadBytesShort() []byte {
|
||||
return r.ReadBytes(int(r.ReadUInt16()))
|
||||
}
|
||||
|
||||
func (r *Reader) ReadUInt16() uint16 {
|
||||
f, _ := r.buf.ReadByte()
|
||||
s, err := r.buf.ReadByte()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return uint16((int32(f) << 8) + int32(s))
|
||||
}
|
||||
|
||||
func (r *Reader) ReadInt32() int32 {
|
||||
b := r.ReadBytes(4)
|
||||
return (int32(b[0]) << 24) | (int32(b[1]) << 16) | (int32(b[2]) << 8) | int32(b[3])
|
||||
}
|
||||
|
||||
func (r *Reader) ReadString() string {
|
||||
data := r.ReadBytes(int(r.ReadInt32() - 4))
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func (r *Reader) ReadStringShort() string {
|
||||
data := r.ReadBytes(int(r.ReadUInt16()))
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func (r *Reader) ReadStringLimit(limit int) string {
|
||||
data := r.ReadBytes(limit)
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func (r *Reader) ReadAvailable() []byte {
|
||||
return r.ReadBytes(r.buf.Len())
|
||||
}
|
||||
|
||||
func (r *Reader) ReadTlvMap(tagSize int) (m TlvMap) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// TODO: error
|
||||
}
|
||||
}()
|
||||
m = make(map[uint16][]byte)
|
||||
for {
|
||||
if r.Len() < tagSize {
|
||||
return m
|
||||
}
|
||||
var k uint16
|
||||
if tagSize == 1 {
|
||||
k = uint16(r.ReadByte())
|
||||
} else if tagSize == 2 {
|
||||
k = r.ReadUInt16()
|
||||
} else if tagSize == 4 {
|
||||
k = uint16(r.ReadInt32())
|
||||
}
|
||||
if k == 255 {
|
||||
return m
|
||||
}
|
||||
m[k] = r.ReadBytes(int(r.ReadUInt16()))
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reader) Len() int {
|
||||
return r.buf.Len()
|
||||
}
|
||||
|
||||
func (tlv TlvMap) Exists(key uint16) bool {
|
||||
if _, ok := tlv[key]; ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// --- Network reader ---
|
||||
|
||||
func NewNetworkReader(conn net.Conn) *NetworkReader {
|
||||
return &NetworkReader{conn: conn}
|
||||
}
|
||||
|
||||
func (r *NetworkReader) ReadByte() byte {
|
||||
buf := make([]byte, 1)
|
||||
n, err := r.conn.Read(buf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if n != 1 {
|
||||
return r.ReadByte()
|
||||
}
|
||||
return buf[0]
|
||||
}
|
||||
|
||||
func (r *NetworkReader) ReadBytes(len int) []byte {
|
||||
buf := make([]byte, len)
|
||||
for i := 0; i < len; i++ {
|
||||
buf[i] = r.ReadByte()
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
func (r *NetworkReader) ReadInt32() int32 {
|
||||
return (int32(r.ReadByte()) << 24) | (int32(r.ReadByte()) << 16) | (int32(r.ReadByte()) << 8) | int32(r.ReadByte())
|
||||
}
|
143
binary/tea.go
Normal file
143
binary/tea.go
Normal file
@ -0,0 +1,143 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
delta = uint32(0x9E3779B9)
|
||||
fillnor = 0xF8
|
||||
)
|
||||
|
||||
type teaCipher struct {
|
||||
keys [4]uint32
|
||||
value []byte
|
||||
byte8 [8]byte
|
||||
ubyte32 [2]uint32
|
||||
xor [8]byte //xor
|
||||
fxor [8]byte //first xor
|
||||
lxor [8]byte //last xor
|
||||
nxor [8]byte //new xor Decrypt add
|
||||
balebuff *bytes.Buffer
|
||||
seedrand *rand.Rand
|
||||
}
|
||||
|
||||
func NewTeaCipher(key []byte) *teaCipher {
|
||||
if len(key) != 16 {
|
||||
return nil
|
||||
}
|
||||
cipher := &teaCipher{
|
||||
balebuff: bytes.NewBuffer(nil),
|
||||
}
|
||||
for i := 0; i < 4; i++ {
|
||||
cipher.keys[i] = binary.BigEndian.Uint32(key[i*4:])
|
||||
}
|
||||
cipher.seedrand = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
return cipher
|
||||
}
|
||||
|
||||
func (c *teaCipher) Encrypt(value []byte) []byte {
|
||||
c.balebuff.Reset()
|
||||
vl := len(value)
|
||||
filln := (8 - (vl + 2)) % 8
|
||||
if filln < 0 {
|
||||
filln += 2 + 8
|
||||
} else {
|
||||
filln += 2
|
||||
}
|
||||
bindex := filln + 1
|
||||
if bindex <= 0 {
|
||||
return nil
|
||||
}
|
||||
rands := make([]byte, bindex)
|
||||
for i := 1; i < bindex; i++ {
|
||||
rands[i] = byte((c.seedrand.Intn(236) + 1))
|
||||
}
|
||||
rands[0] = byte((filln - 2) | fillnor)
|
||||
c.balebuff.Write(rands)
|
||||
c.balebuff.Write(value)
|
||||
c.balebuff.Write([]byte{00, 00, 00, 00, 00, 00, 00})
|
||||
vl = c.balebuff.Len()
|
||||
c.value = c.balebuff.Bytes()
|
||||
c.balebuff.Reset()
|
||||
for i := 0; i < vl; i += 8 {
|
||||
c.xor = xor(c.value[i:i+8], c.fxor[0:8])
|
||||
c.ubyte32[0] = binary.BigEndian.Uint32(c.xor[0:4])
|
||||
c.ubyte32[1] = binary.BigEndian.Uint32(c.xor[4:8])
|
||||
c.encipher()
|
||||
c.fxor = xor(c.byte8[0:8], c.lxor[0:8])
|
||||
c.balebuff.Write(c.fxor[0:8])
|
||||
c.lxor = c.xor
|
||||
|
||||
}
|
||||
return c.balebuff.Bytes()
|
||||
}
|
||||
|
||||
func (c *teaCipher) Decrypt(value []byte) []byte {
|
||||
vl := len(value)
|
||||
if vl <= 0 || (vl%8) != 0 {
|
||||
return nil
|
||||
}
|
||||
c.balebuff.Reset()
|
||||
c.ubyte32[0] = binary.BigEndian.Uint32(value[0:4])
|
||||
c.ubyte32[1] = binary.BigEndian.Uint32(value[4:8])
|
||||
c.decipher()
|
||||
copy(c.lxor[0:8], value[0:8])
|
||||
c.fxor = c.byte8
|
||||
pos := int((c.byte8[0] & 0x7) + 2)
|
||||
c.balebuff.Write(c.byte8[0:8])
|
||||
for i := 8; i < vl; i += 8 {
|
||||
c.xor = xor(value[i:i+8], c.fxor[0:8])
|
||||
c.ubyte32[0] = binary.BigEndian.Uint32(c.xor[0:4])
|
||||
c.ubyte32[1] = binary.BigEndian.Uint32(c.xor[4:8])
|
||||
c.decipher()
|
||||
c.nxor = xor(c.byte8[0:8], c.lxor[0:8])
|
||||
c.balebuff.Write(c.nxor[0:8])
|
||||
c.fxor = xor(c.nxor[0:8], c.lxor[0:8])
|
||||
copy(c.lxor[0:8], value[i:i+8])
|
||||
}
|
||||
pos++
|
||||
c.value = c.balebuff.Bytes()
|
||||
nl := c.balebuff.Len()
|
||||
if pos >= c.balebuff.Len() || (nl-7) <= pos {
|
||||
return nil
|
||||
}
|
||||
return c.value[pos : nl-7]
|
||||
}
|
||||
|
||||
func (c *teaCipher) encipher() {
|
||||
sum := delta
|
||||
for i := 0x10; i > 0; i-- {
|
||||
c.ubyte32[0] += ((c.ubyte32[1] << 4 & 0xFFFFFFF0) + c.keys[0]) ^ (c.ubyte32[1] + sum) ^ ((c.ubyte32[1] >> 5 & 0x07ffffff) + c.keys[1])
|
||||
c.ubyte32[1] += ((c.ubyte32[0] << 4 & 0xFFFFFFF0) + c.keys[2]) ^ (c.ubyte32[0] + sum) ^ ((c.ubyte32[0] >> 5 & 0x07ffffff) + c.keys[3])
|
||||
sum += delta
|
||||
}
|
||||
binary.BigEndian.PutUint32(c.byte8[0:4], c.ubyte32[0])
|
||||
binary.BigEndian.PutUint32(c.byte8[4:8], c.ubyte32[1])
|
||||
}
|
||||
|
||||
func (c *teaCipher) decipher() {
|
||||
sum := delta
|
||||
sum = (sum << 4) & 0xffffffff
|
||||
|
||||
for i := 0x10; i > 0; i-- {
|
||||
c.ubyte32[1] -= (((c.ubyte32[0] << 4 & 0xFFFFFFF0) + c.keys[2]) ^ (c.ubyte32[0] + sum) ^ ((c.ubyte32[0] >> 5 & 0x07ffffff) + c.keys[3]))
|
||||
c.ubyte32[1] &= 0xffffffff
|
||||
c.ubyte32[0] -= (((c.ubyte32[1] << 4 & 0xFFFFFFF0) + c.keys[0]) ^ (c.ubyte32[1] + sum) ^ ((c.ubyte32[1] >> 5 & 0x07ffffff) + c.keys[1]))
|
||||
c.ubyte32[0] &= 0xffffffff
|
||||
sum -= delta
|
||||
}
|
||||
binary.BigEndian.PutUint32(c.byte8[0:4], c.ubyte32[0])
|
||||
binary.BigEndian.PutUint32(c.byte8[4:8], c.ubyte32[1])
|
||||
}
|
||||
|
||||
func xor(a, b []byte) (bts [8]byte) {
|
||||
l := len(a)
|
||||
for i := 0; i < l; i += 4 {
|
||||
binary.BigEndian.PutUint32(bts[i:i+4], binary.BigEndian.Uint32(a[i:i+4])^binary.BigEndian.Uint32(b[i:i+4]))
|
||||
}
|
||||
return bts
|
||||
}
|
77
binary/utils.go
Normal file
77
binary/utils.go
Normal file
@ -0,0 +1,77 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"crypto/rand"
|
||||
binary2 "encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ZlibUncompress(src []byte) []byte {
|
||||
b := bytes.NewReader(src)
|
||||
var out bytes.Buffer
|
||||
r, _ := zlib.NewReader(b)
|
||||
io.Copy(&out, r)
|
||||
return out.Bytes()
|
||||
}
|
||||
|
||||
func RandomString(len int) string {
|
||||
var res string
|
||||
var str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
|
||||
b := bytes.NewBufferString(str)
|
||||
length := b.Len()
|
||||
bigInt := big.NewInt(int64(length))
|
||||
for i := 0; i < len; i++ {
|
||||
randomInt, _ := rand.Int(rand.Reader, bigInt)
|
||||
res += string(str[randomInt.Int64()])
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func CalculateImageResourceId(md5 []byte) string {
|
||||
return strings.ToUpper(fmt.Sprintf(
|
||||
"{%s-%s-%s-%s-%s}.png",
|
||||
hex.EncodeToString(md5[0:4]), hex.EncodeToString(md5[4:6]), hex.EncodeToString(md5[6:8]),
|
||||
hex.EncodeToString(md5[8:10]), hex.EncodeToString(md5[10:]),
|
||||
))
|
||||
}
|
||||
|
||||
func ToIPV4Address(arr []byte) string {
|
||||
if len(arr) != 4 {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%d.%d.%d.%d", arr[0], arr[1], arr[2], arr[3])
|
||||
}
|
||||
|
||||
func UInt32ToIPV4Address(i uint32) string {
|
||||
addr := make([]byte, 4)
|
||||
binary2.LittleEndian.PutUint32(addr, i)
|
||||
return ToIPV4Address(addr)
|
||||
}
|
||||
|
||||
func ToChunkedBytesF(b []byte, size int, f func([]byte)) {
|
||||
r := NewReader(b)
|
||||
for r.Len() >= size {
|
||||
f(r.ReadBytes(size))
|
||||
}
|
||||
if r.Len() > 0 {
|
||||
f(r.ReadAvailable())
|
||||
}
|
||||
}
|
||||
|
||||
func ToBytes(i interface{}) []byte {
|
||||
return NewWriterF(func(w *Writer) {
|
||||
// TODO: more types
|
||||
switch t := i.(type) {
|
||||
case int16:
|
||||
w.WriteUInt16(uint16(t))
|
||||
case int32:
|
||||
w.WriteUInt32(uint32(t))
|
||||
}
|
||||
})
|
||||
}
|
112
binary/writer.go
Normal file
112
binary/writer.go
Normal file
@ -0,0 +1,112 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
type Writer struct {
|
||||
buf *bytes.Buffer
|
||||
}
|
||||
|
||||
func NewWriter() *Writer {
|
||||
return &Writer{buf: new(bytes.Buffer)}
|
||||
}
|
||||
|
||||
func NewWriterF(f func(writer *Writer)) []byte {
|
||||
w := NewWriter()
|
||||
f(w)
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
func (w *Writer) Write(b []byte) {
|
||||
w.buf.Write(b)
|
||||
}
|
||||
|
||||
func (w *Writer) WriteByte(b byte) {
|
||||
w.buf.WriteByte(b)
|
||||
}
|
||||
|
||||
func (w *Writer) WriteUInt16(v uint16) {
|
||||
b := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(b, v)
|
||||
w.Write(b)
|
||||
}
|
||||
|
||||
func (w *Writer) WriteUInt32(v uint32) {
|
||||
b := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(b, v)
|
||||
w.Write(b)
|
||||
}
|
||||
|
||||
func (w *Writer) WriteUInt64(v uint64) {
|
||||
b := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(b, v)
|
||||
w.Write(b)
|
||||
}
|
||||
|
||||
func (w *Writer) WriteString(v string) {
|
||||
payload := []byte(v)
|
||||
w.WriteUInt32(uint32(len(payload) + 4))
|
||||
w.Write(payload)
|
||||
}
|
||||
|
||||
func (w *Writer) WriteStringShort(v string) {
|
||||
w.WriteTlv([]byte(v))
|
||||
}
|
||||
|
||||
func (w *Writer) WriteBool(b bool) {
|
||||
if b {
|
||||
w.WriteByte(0x01)
|
||||
} else {
|
||||
w.WriteByte(0x00)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Writer) EncryptAndWrite(key []byte, data []byte) {
|
||||
tea := NewTeaCipher(key)
|
||||
ed := tea.Encrypt(data)
|
||||
w.Write(ed)
|
||||
}
|
||||
|
||||
func (w *Writer) WriteIntLvPacket(offset int, f func(writer *Writer)) {
|
||||
t := NewWriter()
|
||||
f(t)
|
||||
data := t.Bytes()
|
||||
w.WriteUInt32(uint32(len(data) + offset))
|
||||
w.Write(data)
|
||||
}
|
||||
|
||||
func (w *Writer) WriteUniPacket(commandName string, sessionId, extraData, body []byte) {
|
||||
w.WriteIntLvPacket(4, func(w *Writer) {
|
||||
w.WriteString(commandName)
|
||||
w.WriteUInt32(8)
|
||||
w.Write(sessionId)
|
||||
if len(extraData) == 0 {
|
||||
w.WriteUInt32(0x04)
|
||||
} else {
|
||||
w.WriteUInt32(uint32(len(extraData) + 4))
|
||||
w.Write(extraData)
|
||||
}
|
||||
})
|
||||
w.WriteIntLvPacket(4, func(w *Writer) {
|
||||
w.Write(body)
|
||||
})
|
||||
}
|
||||
|
||||
func (w *Writer) WriteTlv(data []byte) {
|
||||
w.WriteUInt16(uint16(len(data)))
|
||||
w.Write(data)
|
||||
}
|
||||
|
||||
func (w *Writer) WriteTlvLimitedSize(data []byte, limit int) {
|
||||
if len(data) <= limit {
|
||||
w.WriteTlv(data)
|
||||
return
|
||||
}
|
||||
w.WriteTlv(data[:limit])
|
||||
}
|
||||
|
||||
func (w *Writer) Bytes() []byte {
|
||||
return w.buf.Bytes()
|
||||
}
|
418
client/builders.go
Normal file
418
client/builders.go
Normal file
@ -0,0 +1,418 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
"github.com/Mrs4s/MiraiGo/binary/jce"
|
||||
"github.com/Mrs4s/MiraiGo/client/pb"
|
||||
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
||||
"github.com/Mrs4s/MiraiGo/message"
|
||||
"github.com/Mrs4s/MiraiGo/protocol/crypto"
|
||||
"github.com/Mrs4s/MiraiGo/protocol/packets"
|
||||
"github.com/Mrs4s/MiraiGo/protocol/tlv"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func (c *QQClient) buildLoginPacket() (uint16, []byte) {
|
||||
seq := c.nextSeq()
|
||||
req := packets.BuildOicqRequestPacket(c.Uin, 0x0810, crypto.ECDH, c.RandomKey, func(w *binary.Writer) {
|
||||
w.WriteUInt16(9)
|
||||
w.WriteUInt16(17)
|
||||
|
||||
w.Write(tlv.T18(16, uint32(c.Uin)))
|
||||
w.Write(tlv.T1(uint32(c.Uin), SystemDeviceInfo.IpAddress))
|
||||
w.Write(tlv.T106(uint32(c.Uin), 0, c.PasswordMd5, true, SystemDeviceInfo.Guid, SystemDeviceInfo.TgtgtKey))
|
||||
w.Write(tlv.T116(184024956, 0x10400))
|
||||
w.Write(tlv.T100())
|
||||
w.Write(tlv.T107(0))
|
||||
w.Write(tlv.T142("com.tencent.mobileqq"))
|
||||
w.Write(tlv.T144(
|
||||
SystemDeviceInfo.AndroidId,
|
||||
SystemDeviceInfo.GenDeviceInfoData(),
|
||||
SystemDeviceInfo.OSType,
|
||||
SystemDeviceInfo.Version.Release,
|
||||
SystemDeviceInfo.SimInfo,
|
||||
SystemDeviceInfo.APN,
|
||||
false, true, false, tlv.GuidFlag(),
|
||||
SystemDeviceInfo.Model,
|
||||
SystemDeviceInfo.Guid,
|
||||
SystemDeviceInfo.Brand,
|
||||
SystemDeviceInfo.TgtgtKey,
|
||||
))
|
||||
|
||||
w.Write(tlv.T145(SystemDeviceInfo.Guid))
|
||||
w.Write(tlv.T147(16, []byte("8.2.7"), []byte{0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6, 0x8D}))
|
||||
/*
|
||||
if (miscBitMap & 0x80) != 0{
|
||||
w.Write(tlv.T166(1))
|
||||
}
|
||||
*/
|
||||
w.Write(tlv.T154(seq))
|
||||
w.Write(tlv.T141(SystemDeviceInfo.SimInfo, SystemDeviceInfo.APN))
|
||||
w.Write(tlv.T8(2052))
|
||||
w.Write(tlv.T511([]string{
|
||||
"tenpay.com", "openmobile.qq.com", "docs.qq.com", "connect.qq.com",
|
||||
"qzone.qq.com", "vip.qq.com", "qun.qq.com", "game.qq.com", "qqweb.qq.com",
|
||||
"office.qq.com", "ti.qq.com", "mail.qq.com", "qzone.com", "mma.qq.com",
|
||||
}))
|
||||
|
||||
w.Write(tlv.T187(SystemDeviceInfo.MacAddress))
|
||||
w.Write(tlv.T188(SystemDeviceInfo.AndroidId))
|
||||
if len(SystemDeviceInfo.IMSIMd5) != 0 {
|
||||
w.Write(tlv.T194(SystemDeviceInfo.IMSIMd5))
|
||||
}
|
||||
w.Write(tlv.T191(0x82))
|
||||
if len(SystemDeviceInfo.WifiBSSID) != 0 && len(SystemDeviceInfo.WifiSSID) != 0 {
|
||||
w.Write(tlv.T202(SystemDeviceInfo.WifiBSSID, SystemDeviceInfo.WifiSSID))
|
||||
}
|
||||
w.Write(tlv.T177())
|
||||
w.Write(tlv.T516())
|
||||
w.Write(tlv.T521())
|
||||
w.Write(tlv.T525(tlv.T536([]byte{0x01, 0x00})))
|
||||
})
|
||||
sso := packets.BuildSsoPacket(seq, "wtlogin.login", SystemDeviceInfo.IMEI, []byte{}, c.OutGoingPacketSessionId, req, c.ksid)
|
||||
packet := packets.BuildLoginPacket(c.Uin, 2, make([]byte, 16), sso, []byte{})
|
||||
return seq, packet
|
||||
}
|
||||
|
||||
func (c *QQClient) buildCaptchaPacket(result string, sign []byte) (uint16, []byte) {
|
||||
seq := c.nextSeq()
|
||||
req := packets.BuildOicqRequestPacket(c.Uin, 0x810, crypto.ECDH, c.RandomKey, func(w *binary.Writer) {
|
||||
w.WriteUInt16(2) // sub command
|
||||
w.WriteUInt16(4)
|
||||
w.Write(tlv.T2(result, sign))
|
||||
w.Write(tlv.T8(2052))
|
||||
w.Write(tlv.T104(c.t104))
|
||||
w.Write(tlv.T116(150470524, 66560))
|
||||
})
|
||||
sso := packets.BuildSsoPacket(seq, "wtlogin.login", SystemDeviceInfo.IMEI, []byte{}, c.OutGoingPacketSessionId, req, c.ksid)
|
||||
packet := packets.BuildLoginPacket(c.Uin, 2, make([]byte, 16), sso, []byte{})
|
||||
return seq, packet
|
||||
}
|
||||
|
||||
func (c *QQClient) buildClientRegisterPacket() (uint16, []byte) {
|
||||
seq := c.nextSeq()
|
||||
svc := &jce.SvcReqRegister{
|
||||
ConnType: 0,
|
||||
Uin: c.Uin,
|
||||
Bid: 1 | 2 | 4,
|
||||
Status: 11,
|
||||
KickPC: 0,
|
||||
KickWeak: 0,
|
||||
IOSVersion: int64(SystemDeviceInfo.Version.Sdk),
|
||||
NetType: 1,
|
||||
RegType: 0,
|
||||
Guid: SystemDeviceInfo.Guid,
|
||||
IsSetStatus: 0,
|
||||
LocaleId: 2052,
|
||||
DevName: string(SystemDeviceInfo.Model),
|
||||
DevType: string(SystemDeviceInfo.Model),
|
||||
OSVer: string(SystemDeviceInfo.Version.Release),
|
||||
OpenPush: 1,
|
||||
LargeSeq: 1551,
|
||||
OldSSOIp: 0,
|
||||
NewSSOIp: 31806887127679168,
|
||||
ChannelNo: "",
|
||||
CPID: 0,
|
||||
VendorName: "MIUI",
|
||||
VendorOSName: "ONEPLUS A5000_23_17",
|
||||
B769: []byte{0x0A, 0x04, 0x08, 0x2E, 0x10, 0x00, 0x0A, 0x05, 0x08, 0x9B, 0x02, 0x10, 0x00},
|
||||
SetMute: 0,
|
||||
}
|
||||
b := append([]byte{0x0A}, svc.ToBytes()...)
|
||||
b = append(b, 0x0B)
|
||||
buf := &jce.RequestDataVersion3{
|
||||
Map: map[string][]byte{"SvcReqRegister": b},
|
||||
}
|
||||
pkt := &jce.RequestPacket{
|
||||
IVersion: 3,
|
||||
SServantName: "PushService",
|
||||
SFuncName: "SvcReqRegister",
|
||||
SBuffer: buf.ToBytes(),
|
||||
Context: make(map[string]string),
|
||||
Status: make(map[string]string),
|
||||
}
|
||||
sso := packets.BuildSsoPacket(seq, "StatSvc.register", SystemDeviceInfo.IMEI, c.sigInfo.tgt, c.OutGoingPacketSessionId, pkt.ToBytes(), c.ksid)
|
||||
packet := packets.BuildLoginPacket(c.Uin, 1, c.sigInfo.d2Key, sso, c.sigInfo.d2)
|
||||
return seq, packet
|
||||
}
|
||||
|
||||
func (c *QQClient) buildPushResponsePacket(t int32, pktSeq int64, jceBuf []byte) (uint16, []byte) {
|
||||
seq := c.nextSeq()
|
||||
req := jce.NewJceWriter()
|
||||
req.WriteInt32(t, 1)
|
||||
req.WriteInt64(pktSeq, 2)
|
||||
req.WriteBytes(jceBuf, 3)
|
||||
b := append([]byte{0x0A}, req.Bytes()...)
|
||||
b = append(b, 0x0B)
|
||||
buf := &jce.RequestDataVersion3{
|
||||
Map: map[string][]byte{"PushResp": b},
|
||||
}
|
||||
pkt := &jce.RequestPacket{
|
||||
IVersion: 3,
|
||||
SServantName: "QQService.ConfigPushSvc.MainServant",
|
||||
SFuncName: "PushResp",
|
||||
SBuffer: buf.ToBytes(),
|
||||
Context: make(map[string]string),
|
||||
Status: make(map[string]string),
|
||||
}
|
||||
packet := packets.BuildUniPacket(c.Uin, seq, "ConfigPushSvc.PushResp", 1, c.OutGoingPacketSessionId, []byte{}, c.sigInfo.d2Key, pkt.ToBytes())
|
||||
return seq, packet
|
||||
}
|
||||
|
||||
func (c *QQClient) buildFriendGroupListRequestPacket(friendStartIndex, friendListCount, groupStartIndex, groupListCount int16) (uint16, []byte) {
|
||||
seq := c.nextSeq()
|
||||
d50, _ := proto.Marshal(&pb.D50ReqBody{
|
||||
Appid: 1002,
|
||||
ReqMusicSwitch: 1,
|
||||
ReqMutualmarkAlienation: 1,
|
||||
ReqKsingSwitch: 1,
|
||||
ReqMutualmarkLbsshare: 1,
|
||||
})
|
||||
req := &jce.FriendListRequest{
|
||||
Reqtype: 3,
|
||||
IfReflush: func() byte { // fuck golang
|
||||
if friendStartIndex <= 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}(),
|
||||
Uin: int64(c.Uin),
|
||||
StartIndex: friendStartIndex,
|
||||
FriendCount: friendListCount,
|
||||
GroupId: 0,
|
||||
IfGetGroupInfo: func() byte {
|
||||
if groupListCount <= 0 {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}(),
|
||||
GroupStartIndex: byte(groupStartIndex),
|
||||
GroupCount: byte(groupListCount),
|
||||
IfGetMSFGroup: 0,
|
||||
IfShowTermType: 1,
|
||||
Version: 27,
|
||||
UinList: nil,
|
||||
AppType: 0,
|
||||
IfGetDOVId: 0,
|
||||
IfGetBothFlag: 0,
|
||||
D50: d50,
|
||||
D6B: []byte{},
|
||||
SnsTypeList: []int64{13580, 13581, 13582},
|
||||
}
|
||||
b := append([]byte{0x0A}, req.ToBytes()...)
|
||||
b = append(b, 0x0B)
|
||||
buf := &jce.RequestDataVersion3{
|
||||
Map: map[string][]byte{"FL": b},
|
||||
}
|
||||
pkt := &jce.RequestPacket{
|
||||
IVersion: 3,
|
||||
CPacketType: 0x003,
|
||||
IRequestId: 1921334514,
|
||||
SServantName: "mqq.IMService.FriendListServiceServantObj",
|
||||
SFuncName: "GetFriendListReq",
|
||||
SBuffer: buf.ToBytes(),
|
||||
Context: make(map[string]string),
|
||||
Status: make(map[string]string),
|
||||
}
|
||||
packet := packets.BuildUniPacket(c.Uin, seq, "friendlist.getFriendGroupList", 1, c.OutGoingPacketSessionId, []byte{}, c.sigInfo.d2Key, pkt.ToBytes())
|
||||
return seq, packet
|
||||
}
|
||||
|
||||
func (c *QQClient) buildGroupListRequestPacket() (uint16, []byte) {
|
||||
seq := c.nextSeq()
|
||||
req := &jce.TroopListRequest{
|
||||
Uin: c.Uin,
|
||||
GetMSFMsgFlag: 1,
|
||||
Cookies: []byte{},
|
||||
GroupInfo: []int64{},
|
||||
GroupFlagExt: 1,
|
||||
Version: 7,
|
||||
CompanyId: 0,
|
||||
VersionNum: 1,
|
||||
GetLongGroupName: 1,
|
||||
}
|
||||
b := append([]byte{0x0A}, req.ToBytes()...)
|
||||
b = append(b, 0x0B)
|
||||
buf := &jce.RequestDataVersion3{
|
||||
Map: map[string][]byte{"GetTroopListReqV2Simplify": b},
|
||||
}
|
||||
pkt := &jce.RequestPacket{
|
||||
IVersion: 3,
|
||||
CPacketType: 0x00,
|
||||
IRequestId: c.nextPacketSeq(),
|
||||
SServantName: "mqq.IMService.FriendListServiceServantObj",
|
||||
SFuncName: "GetTroopListReqV2Simplify",
|
||||
SBuffer: buf.ToBytes(),
|
||||
Context: make(map[string]string),
|
||||
Status: make(map[string]string),
|
||||
}
|
||||
packet := packets.BuildUniPacket(c.Uin, seq, "friendlist.GetTroopListReqV2", 1, c.OutGoingPacketSessionId, []byte{}, c.sigInfo.d2Key, pkt.ToBytes())
|
||||
return seq, packet
|
||||
}
|
||||
|
||||
func (c *QQClient) buildGroupMemberListRequestPacket(groupUin, groupCode, nextUin int64) (uint16, []byte) {
|
||||
seq := c.nextSeq()
|
||||
req := &jce.TroopMemberListRequest{
|
||||
Uin: c.Uin,
|
||||
GroupCode: groupCode,
|
||||
NextUin: nextUin,
|
||||
GroupUin: groupUin,
|
||||
Version: 2,
|
||||
}
|
||||
b := append([]byte{0x0A}, req.ToBytes()...)
|
||||
b = append(b, 0x0B)
|
||||
buf := &jce.RequestDataVersion3{
|
||||
Map: map[string][]byte{"GTML": b},
|
||||
}
|
||||
pkt := &jce.RequestPacket{
|
||||
IVersion: 3,
|
||||
IRequestId: c.nextPacketSeq(),
|
||||
SServantName: "mqq.IMService.FriendListServiceServantObj",
|
||||
SFuncName: "GetTroopMemberListReq",
|
||||
SBuffer: buf.ToBytes(),
|
||||
Context: make(map[string]string),
|
||||
Status: make(map[string]string),
|
||||
}
|
||||
packet := packets.BuildUniPacket(c.Uin, seq, "friendlist.GetTroopMemberListReq", 1, c.OutGoingPacketSessionId, []byte{}, c.sigInfo.d2Key, pkt.ToBytes())
|
||||
return seq, packet
|
||||
}
|
||||
|
||||
func (c *QQClient) buildGetMessageRequestPacket(flag msg.SyncFlag, msgTime int64) (uint16, []byte) {
|
||||
seq := c.nextSeq()
|
||||
cook := c.syncCookie
|
||||
if cook == nil {
|
||||
cook, _ = proto.Marshal(&msg.SyncCookie{
|
||||
Time: msgTime,
|
||||
Ran1: 758330138,
|
||||
Ran2: 2480149246,
|
||||
Const1: 1167238020,
|
||||
Const2: 3913056418,
|
||||
Const3: 0x1D,
|
||||
})
|
||||
}
|
||||
req := &msg.GetMessageRequest{
|
||||
SyncFlag: flag,
|
||||
SyncCookie: cook,
|
||||
LatestRambleNumber: 20,
|
||||
OtherRambleNumber: 3,
|
||||
OnlineSyncFlag: 1,
|
||||
ContextFlag: 1,
|
||||
MsgReqType: 1,
|
||||
PubaccountCookie: []byte{},
|
||||
MsgCtrlBuf: []byte{},
|
||||
ServerBuf: []byte{},
|
||||
}
|
||||
payload, _ := proto.Marshal(req)
|
||||
packet := packets.BuildUniPacket(c.Uin, seq, "MessageSvc.PbGetMsg", 1, c.OutGoingPacketSessionId, []byte{}, c.sigInfo.d2Key, payload)
|
||||
return seq, packet
|
||||
}
|
||||
|
||||
func (c *QQClient) buildStopGetMessagePacket(msgTime int64) []byte {
|
||||
_, pkt := c.buildGetMessageRequestPacket(msg.SyncFlag_STOP, msgTime)
|
||||
return pkt
|
||||
}
|
||||
|
||||
func (c *QQClient) buildDeleteMessageRequestPacket(msg []*pb.MessageItem) (uint16, []byte) {
|
||||
seq := c.nextSeq()
|
||||
req := &pb.DeleteMessageRequest{Items: msg}
|
||||
payload, _ := proto.Marshal(req)
|
||||
packet := packets.BuildUniPacket(c.Uin, seq, "MessageSvc.PbDeleteMsg", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
||||
return seq, packet
|
||||
}
|
||||
|
||||
func (c *QQClient) buildGroupSendingPacket(groupCode int64, m *message.SendingMessage) (uint16, []byte) {
|
||||
seq := c.nextSeq()
|
||||
req := &msg.SendMessageRequest{
|
||||
RoutingHead: &msg.RoutingHead{Grp: &msg.Grp{GroupCode: groupCode}},
|
||||
ContentHead: &msg.ContentHead{PkgNum: 1},
|
||||
MsgBody: &msg.MessageBody{
|
||||
RichText: &msg.RichText{
|
||||
Elems: message.ToProtoElems(m.Elements),
|
||||
},
|
||||
},
|
||||
MsgSeq: c.nextMessageSeq(),
|
||||
MsgRand: int32(rand.Uint32()),
|
||||
SyncCookie: EmptyBytes,
|
||||
MsgVia: 1,
|
||||
MsgCtrl: nil,
|
||||
}
|
||||
payload, _ := proto.Marshal(req)
|
||||
packet := packets.BuildUniPacket(c.Uin, seq, "MessageSvc.PbSendMsg", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
||||
return seq, packet
|
||||
}
|
||||
|
||||
func (c *QQClient) buildGroupImageStorePacket(groupCode int64, md5 [16]byte, size int32) (uint16, []byte) {
|
||||
seq := c.nextSeq()
|
||||
name := binary.RandomString(16) + ".gif"
|
||||
req := &pb.D388ReqBody{
|
||||
NetType: 3,
|
||||
Subcmd: 1,
|
||||
MsgTryupImgReq: []*pb.TryUpImgReq{
|
||||
{
|
||||
GroupCode: groupCode,
|
||||
SrcUin: c.Uin,
|
||||
FileMd5: md5[:],
|
||||
FileSize: int64(size),
|
||||
FileName: name,
|
||||
SrcTerm: 5,
|
||||
PlatformType: 9,
|
||||
BuType: 1,
|
||||
PicType: 1000,
|
||||
BuildVer: "8.2.7.4410",
|
||||
AppPicType: 1006,
|
||||
FileIndex: EmptyBytes,
|
||||
TransferUrl: EmptyBytes,
|
||||
},
|
||||
},
|
||||
Extension: EmptyBytes,
|
||||
}
|
||||
payload, _ := proto.Marshal(req)
|
||||
packet := packets.BuildUniPacket(c.Uin, seq, "ImgStore.GroupPicUp", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
||||
return seq, packet
|
||||
}
|
||||
|
||||
func (c *QQClient) buildImageUploadPacket(data, updKey []byte, commandId int32, fmd5 [16]byte) (r [][]byte) {
|
||||
offset := 0
|
||||
binary.ToChunkedBytesF(data, 8192*8, func(chunked []byte) {
|
||||
w := binary.NewWriter()
|
||||
cmd5 := md5.Sum(chunked)
|
||||
head, _ := proto.Marshal(&pb.ReqDataHighwayHead{
|
||||
MsgBasehead: &pb.DataHighwayHead{
|
||||
Version: 1,
|
||||
Uin: strconv.FormatInt(c.Uin, 10),
|
||||
Command: "PicUp.DataUp",
|
||||
Seq: func() int32 {
|
||||
if commandId == 2 {
|
||||
return c.nextGroupDataTransSeq()
|
||||
}
|
||||
return c.nextGroupDataTransSeq()
|
||||
}(),
|
||||
Appid: 537062409,
|
||||
Dataflag: 4096,
|
||||
CommandId: commandId,
|
||||
LocaleId: 2052,
|
||||
},
|
||||
MsgSeghead: &pb.SegHead{
|
||||
Filesize: int64(len(data)),
|
||||
Dataoffset: int64(offset),
|
||||
Datalength: int32(len(chunked)),
|
||||
Serviceticket: updKey,
|
||||
Md5: cmd5[:],
|
||||
FileMd5: fmd5[:],
|
||||
},
|
||||
ReqExtendinfo: EmptyBytes,
|
||||
})
|
||||
offset += len(chunked)
|
||||
w.WriteByte(40)
|
||||
w.WriteUInt32(uint32(len(head)))
|
||||
w.WriteUInt32(uint32(len(chunked)))
|
||||
w.Write(head)
|
||||
w.Write(chunked)
|
||||
w.WriteByte(41)
|
||||
r = append(r, w.Bytes())
|
||||
})
|
||||
return
|
||||
}
|
409
client/client.go
Normal file
409
client/client.go
Normal file
@ -0,0 +1,409 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
"github.com/Mrs4s/MiraiGo/client/pb"
|
||||
"github.com/Mrs4s/MiraiGo/message"
|
||||
"github.com/Mrs4s/MiraiGo/protocol/packets"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type QQClient struct {
|
||||
Uin int64
|
||||
PasswordMd5 [16]byte
|
||||
|
||||
Nickname string
|
||||
Age uint16
|
||||
Gender uint16
|
||||
FriendList []*FriendInfo
|
||||
GroupList []*GroupInfo
|
||||
|
||||
SequenceId uint16
|
||||
OutGoingPacketSessionId []byte
|
||||
RandomKey []byte
|
||||
Conn net.Conn
|
||||
|
||||
decoders map[string]func(*QQClient, []byte) (interface{}, error)
|
||||
handlers map[uint16]func(interface{}, error)
|
||||
|
||||
syncCookie []byte
|
||||
pubAccountCookie []byte
|
||||
msgCtrlBuf []byte
|
||||
ksid []byte
|
||||
t104 []byte
|
||||
t150 []byte
|
||||
t149 []byte
|
||||
t528 []byte
|
||||
t530 []byte
|
||||
rollbackSig []byte
|
||||
timeDiff int64
|
||||
sigInfo *loginSigInfo
|
||||
pwdFlag bool
|
||||
running bool
|
||||
|
||||
lastMessageSeq int32
|
||||
requestPacketRequestId int32
|
||||
messageSeq int32
|
||||
groupDataTransSeq int32
|
||||
|
||||
privateMessageHandlers []func(*QQClient, *message.PrivateMessage)
|
||||
groupMessageHandlers []func(*QQClient, *message.GroupMessage)
|
||||
}
|
||||
|
||||
type loginSigInfo struct {
|
||||
loginBitmap uint64
|
||||
tgt []byte
|
||||
tgtKey []byte
|
||||
|
||||
userStKey []byte
|
||||
userStWebSig []byte
|
||||
sKey []byte
|
||||
d2 []byte
|
||||
d2Key []byte
|
||||
wtSessionTicketKey []byte
|
||||
deviceToken []byte
|
||||
}
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
}
|
||||
|
||||
// NewClient create new qq client
|
||||
func NewClient(uin int64, password string) *QQClient {
|
||||
cli := &QQClient{
|
||||
Uin: uin,
|
||||
PasswordMd5: md5.Sum([]byte(password)),
|
||||
SequenceId: 0x3635,
|
||||
RandomKey: make([]byte, 16),
|
||||
OutGoingPacketSessionId: []byte{0x02, 0xB0, 0x5B, 0x8B},
|
||||
decoders: map[string]func(*QQClient, []byte) (interface{}, error){
|
||||
"wtlogin.login": decodeLoginResponse,
|
||||
"StatSvc.register": decodeClientRegisterResponse,
|
||||
"MessageSvc.PushNotify": decodeSvcNotify,
|
||||
"OnlinePush.PbPushGroupMsg": decodeGroupMessagePacket,
|
||||
"ConfigPushSvc.PushReq": decodePushReqPacket,
|
||||
"MessageSvc.PbGetMsg": decodeMessageSvcPacket,
|
||||
"friendlist.getFriendGroupList": decodeFriendGroupListResponse,
|
||||
"friendlist.GetTroopListReqV2": decodeGroupListResponse,
|
||||
"friendlist.GetTroopMemberListReq": decodeGroupMemberListResponse,
|
||||
"ImgStore.GroupPicUp": decodeGroupImageStoreResponse,
|
||||
},
|
||||
handlers: map[uint16]func(interface{}, error){},
|
||||
sigInfo: &loginSigInfo{},
|
||||
requestPacketRequestId: 1921334513,
|
||||
messageSeq: 22911,
|
||||
ksid: []byte("|454001228437590|A8.2.7.27f6ea96"),
|
||||
}
|
||||
rand.Read(cli.RandomKey)
|
||||
return cli
|
||||
}
|
||||
|
||||
// Login send login request
|
||||
func (c *QQClient) Login() (*LoginResponse, error) {
|
||||
if c.running {
|
||||
return nil, ErrAlreadyRunning
|
||||
}
|
||||
conn, err := net.Dial("tcp", "125.94.60.146:80") //TODO: more servers
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Conn = conn
|
||||
c.running = true
|
||||
go c.loop()
|
||||
seq, packet := c.buildLoginPacket()
|
||||
rsp, err := c.sendAndWait(seq, packet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := rsp.(LoginResponse)
|
||||
if l.Success {
|
||||
c.registerClient()
|
||||
go c.heartbeat()
|
||||
}
|
||||
return &l, nil
|
||||
}
|
||||
|
||||
// SubmitCaptcha send captcha to server
|
||||
func (c *QQClient) SubmitCaptcha(result string, sign []byte) (*LoginResponse, error) {
|
||||
seq, packet := c.buildCaptchaPacket(result, sign)
|
||||
rsp, err := c.sendAndWait(seq, packet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := rsp.(LoginResponse)
|
||||
if l.Success {
|
||||
c.registerClient()
|
||||
go c.heartbeat()
|
||||
}
|
||||
return &l, nil
|
||||
}
|
||||
|
||||
// ReloadFriendList refresh QQClient.FriendList field via GetFriendList()
|
||||
func (c *QQClient) ReloadFriendList() error {
|
||||
rsp, err := c.GetFriendList()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.FriendList = rsp.List
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFriendList request friend list
|
||||
func (c *QQClient) GetFriendList() (*FriendListResponse, error) {
|
||||
var curFriendCount = 0
|
||||
r := &FriendListResponse{}
|
||||
for {
|
||||
rsp, err := c.sendAndWait(c.buildFriendGroupListRequestPacket(int16(curFriendCount), 150, 0, 0))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list := rsp.(FriendListResponse)
|
||||
r.TotalCount = list.TotalCount
|
||||
r.List = append(r.List, list.List...)
|
||||
curFriendCount += len(list.List)
|
||||
if int32(curFriendCount) >= r.TotalCount {
|
||||
break
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (c *QQClient) SendGroupMessage(groupCode int64, m *message.SendingMessage) {
|
||||
_, pkt := c.buildGroupSendingPacket(groupCode, m)
|
||||
c.send(pkt)
|
||||
}
|
||||
|
||||
func (c *QQClient) UploadGroupImage(groupCode int64, img []byte) (*message.GroupImageElement, error) {
|
||||
h := md5.Sum(img)
|
||||
seq, pkt := c.buildGroupImageStorePacket(groupCode, h, int32(len(img)))
|
||||
r, err := c.sendAndWait(seq, pkt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rsp := r.(groupImageUploadResponse)
|
||||
if rsp.ResultCode != 0 {
|
||||
return nil, errors.New(rsp.Message)
|
||||
}
|
||||
if rsp.IsExists {
|
||||
return message.NewGroupImage(binary.CalculateImageResourceId(h[:]), h[:]), nil
|
||||
}
|
||||
for i, ip := range rsp.UploadIp {
|
||||
updServer := binary.UInt32ToIPV4Address(uint32(ip))
|
||||
conn, err := net.DialTimeout("tcp", updServer+":"+strconv.FormatInt(int64(rsp.UploadPort[i]), 10), time.Second*5)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if conn.SetDeadline(time.Now().Add(time.Second*10)) != nil {
|
||||
_ = conn.Close()
|
||||
continue
|
||||
}
|
||||
pkt := c.buildImageUploadPacket(img, rsp.UploadKey, 2, h)
|
||||
for _, p := range pkt {
|
||||
_, err = conn.Write(p)
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
r := binary.NewNetworkReader(conn)
|
||||
r.ReadByte()
|
||||
hl := r.ReadInt32()
|
||||
r.ReadBytes(4)
|
||||
payload := r.ReadBytes(int(hl))
|
||||
_ = conn.Close()
|
||||
rsp := pb.RspDataHighwayHead{}
|
||||
if proto.Unmarshal(payload, &rsp) != nil {
|
||||
continue
|
||||
}
|
||||
if rsp.ErrorCode != 0 {
|
||||
return nil, errors.New("upload failed")
|
||||
}
|
||||
return message.NewGroupImage(binary.CalculateImageResourceId(h[:]), h[:]), nil
|
||||
}
|
||||
return nil, errors.New("upload failed")
|
||||
}
|
||||
|
||||
func (c *QQClient) ReloadGroupList() error {
|
||||
list, err := c.GetGroupList()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.GroupList = list
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *QQClient) GetGroupList() ([]*GroupInfo, error) {
|
||||
rsp, err := c.sendAndWait(c.buildGroupListRequestPacket())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := rsp.([]*GroupInfo)
|
||||
for _, group := range r {
|
||||
m, err := c.GetGroupMembers(group)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
group.Members = m
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (c *QQClient) GetGroupMembers(group *GroupInfo) ([]GroupMemberInfo, error) {
|
||||
var nextUin int64
|
||||
var list []GroupMemberInfo
|
||||
for {
|
||||
data, err := c.sendAndWait(c.buildGroupMemberListRequestPacket(group.Uin, group.Code, nextUin))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rsp := data.(groupMemberListResponse)
|
||||
nextUin = rsp.NextUin
|
||||
list = append(list, rsp.list...)
|
||||
if nextUin == 0 {
|
||||
return list, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *QQClient) FindFriend(uin int64) *FriendInfo {
|
||||
for _, t := range c.FriendList {
|
||||
f := t
|
||||
if f.Uin == uin {
|
||||
return f
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *QQClient) FindGroup(uin int64) *GroupInfo {
|
||||
for _, g := range c.GroupList {
|
||||
f := g
|
||||
if f.Uin == uin {
|
||||
return f
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GroupInfo) FindMember(uin int64) *GroupMemberInfo {
|
||||
for _, m := range g.Members {
|
||||
f := m
|
||||
if f.Uin == uin {
|
||||
return &f
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *QQClient) registerClient() {
|
||||
seq, packet := c.buildClientRegisterPacket()
|
||||
_, _ = c.sendAndWait(seq, packet)
|
||||
}
|
||||
|
||||
func (c *QQClient) nextSeq() uint16 {
|
||||
c.SequenceId++
|
||||
c.SequenceId &= 0x7FFF
|
||||
if c.SequenceId == 0 {
|
||||
c.SequenceId++
|
||||
}
|
||||
return c.SequenceId
|
||||
}
|
||||
|
||||
func (c *QQClient) nextPacketSeq() int32 {
|
||||
s := atomic.LoadInt32(&c.requestPacketRequestId)
|
||||
atomic.AddInt32(&c.requestPacketRequestId, 2)
|
||||
return s
|
||||
}
|
||||
|
||||
func (c *QQClient) nextMessageSeq() int32 {
|
||||
s := atomic.LoadInt32(&c.messageSeq)
|
||||
atomic.AddInt32(&c.messageSeq, 2)
|
||||
return s
|
||||
}
|
||||
|
||||
func (c *QQClient) nextGroupDataTransSeq() int32 {
|
||||
s := atomic.LoadInt32(&c.groupDataTransSeq)
|
||||
atomic.AddInt32(&c.groupDataTransSeq, 2)
|
||||
return s
|
||||
}
|
||||
|
||||
func (c *QQClient) send(pkt []byte) error {
|
||||
_, err := c.Conn.Write(pkt)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *QQClient) sendAndWait(seq uint16, pkt []byte) (interface{}, error) {
|
||||
type T struct {
|
||||
Response interface{}
|
||||
Error error
|
||||
}
|
||||
_, err := c.Conn.Write(pkt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ch := make(chan T)
|
||||
c.handlers[seq] = func(i interface{}, err error) {
|
||||
ch <- T{
|
||||
Response: i,
|
||||
Error: err,
|
||||
}
|
||||
}
|
||||
rsp := <-ch
|
||||
return rsp.Response, rsp.Error
|
||||
}
|
||||
|
||||
func (c *QQClient) loop() {
|
||||
reader := binary.NewNetworkReader(c.Conn)
|
||||
for c.running {
|
||||
data := reader.ReadBytes(int(reader.ReadInt32()) - 4)
|
||||
pkt, err := packets.ParseIncomingPacket(data, c.sigInfo.d2Key)
|
||||
if err != nil {
|
||||
log.Println("parse incoming packet error: " + err.Error())
|
||||
continue
|
||||
}
|
||||
payload := pkt.Payload
|
||||
if pkt.Flag2 == 2 {
|
||||
payload, err = pkt.DecryptPayload(c.RandomKey)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
fmt.Println(pkt.CommandName)
|
||||
go func() {
|
||||
decoder, ok := c.decoders[pkt.CommandName]
|
||||
if !ok {
|
||||
if f, ok := c.handlers[pkt.SequenceId]; ok {
|
||||
delete(c.handlers, pkt.SequenceId)
|
||||
f(nil, nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
rsp, err := decoder(c, payload)
|
||||
if err != nil {
|
||||
log.Println("decode", pkt.CommandName, "error:", err)
|
||||
}
|
||||
if f, ok := c.handlers[pkt.SequenceId]; ok {
|
||||
delete(c.handlers, pkt.SequenceId)
|
||||
f(rsp, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *QQClient) heartbeat() {
|
||||
for c.running {
|
||||
time.Sleep(time.Second * 30)
|
||||
seq := c.nextSeq()
|
||||
sso := packets.BuildSsoPacket(seq, "Heartbeat.Alive", SystemDeviceInfo.IMEI, []byte{}, c.OutGoingPacketSessionId, []byte{}, c.ksid)
|
||||
packet := packets.BuildLoginPacket(c.Uin, 0, []byte{}, sso, []byte{})
|
||||
_, _ = c.sendAndWait(seq, packet)
|
||||
}
|
||||
}
|
282
client/decoders.go
Normal file
282
client/decoders.go
Normal file
@ -0,0 +1,282 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
"github.com/Mrs4s/MiraiGo/binary/jce"
|
||||
"github.com/Mrs4s/MiraiGo/client/pb"
|
||||
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"time"
|
||||
)
|
||||
|
||||
func decodeLoginResponse(c *QQClient, payload []byte) (interface{}, error) {
|
||||
reader := binary.NewReader(payload)
|
||||
reader.ReadUInt16() // sub command
|
||||
t := reader.ReadByte()
|
||||
reader.ReadUInt16()
|
||||
m := reader.ReadTlvMap(2)
|
||||
if t == 0 { // login success
|
||||
if t150, ok := m[0x150]; ok {
|
||||
c.t150 = t150
|
||||
}
|
||||
if t161, ok := m[0x161]; ok {
|
||||
c.decodeT161(t161)
|
||||
}
|
||||
c.decodeT119(m[0x119])
|
||||
return LoginResponse{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
if t == 2 {
|
||||
c.t104, _ = m[0x104]
|
||||
if m.Exists(0x192) { // slider, not supported yet
|
||||
return LoginResponse{
|
||||
Success: false,
|
||||
Error: UnknownLoginError,
|
||||
}, nil
|
||||
}
|
||||
if m.Exists(0x165) { // image
|
||||
imgData := binary.NewReader(m[0x105])
|
||||
signLen := imgData.ReadUInt16()
|
||||
imgData.ReadUInt16()
|
||||
sign := imgData.ReadBytes(int(signLen))
|
||||
return LoginResponse{
|
||||
Success: false,
|
||||
Error: NeedCaptcha,
|
||||
CaptchaImage: imgData.ReadAvailable(),
|
||||
CaptchaSign: sign,
|
||||
}, nil
|
||||
} else {
|
||||
return LoginResponse{
|
||||
Success: false,
|
||||
Error: UnknownLoginError,
|
||||
}, nil
|
||||
}
|
||||
} // need captcha
|
||||
|
||||
if t == 160 {
|
||||
|
||||
}
|
||||
|
||||
if t == 204 {
|
||||
return LoginResponse{
|
||||
Success: false,
|
||||
Error: DeviceLockError,
|
||||
}, nil
|
||||
} // drive lock
|
||||
|
||||
if t149, ok := m[0x149]; ok {
|
||||
t149r := binary.NewReader(t149)
|
||||
t149r.ReadBytes(2)
|
||||
t149r.ReadStringShort() // title
|
||||
return LoginResponse{
|
||||
Success: false,
|
||||
Error: OtherLoginError,
|
||||
ErrorMessage: t149r.ReadStringShort(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
if t146, ok := m[0x146]; ok {
|
||||
t146r := binary.NewReader(t146)
|
||||
t146r.ReadBytes(4) // ver and code
|
||||
t146r.ReadStringShort() // title
|
||||
return LoginResponse{
|
||||
Success: false,
|
||||
Error: OtherLoginError,
|
||||
ErrorMessage: t146r.ReadStringShort(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, nil // ?
|
||||
}
|
||||
|
||||
func decodeClientRegisterResponse(c *QQClient, payload []byte) (interface{}, error) {
|
||||
request := &jce.RequestPacket{}
|
||||
request.ReadFrom(jce.NewJceReader(payload))
|
||||
data := &jce.RequestDataVersion2{}
|
||||
data.ReadFrom(jce.NewJceReader(request.SBuffer))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func decodePushReqPacket(c *QQClient, payload []byte) (interface{}, error) {
|
||||
request := &jce.RequestPacket{}
|
||||
request.ReadFrom(jce.NewJceReader(payload))
|
||||
data := &jce.RequestDataVersion2{}
|
||||
data.ReadFrom(jce.NewJceReader(request.SBuffer))
|
||||
r := jce.NewJceReader(data.Map["PushReq"]["ConfigPush.PushReq"][1:])
|
||||
jceBuf := []byte{}
|
||||
t := r.ReadInt32(1)
|
||||
r.ReadSlice(&jceBuf, 2)
|
||||
seq := r.ReadInt64(3)
|
||||
_, pkt := c.buildPushResponsePacket(t, seq, jceBuf)
|
||||
return nil, c.send(pkt)
|
||||
}
|
||||
|
||||
func decodeMessageSvcPacket(c *QQClient, payload []byte) (interface{}, error) {
|
||||
rsp := msg.GetMessageResponse{}
|
||||
err := proto.Unmarshal(payload, &rsp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if rsp.Result != 0 {
|
||||
return nil, errors.New("message svc result unsuccessful")
|
||||
}
|
||||
c.syncCookie = rsp.SyncCookie
|
||||
c.pubAccountCookie = rsp.PubAccountCookie
|
||||
c.msgCtrlBuf = rsp.MsgCtrlBuf
|
||||
if rsp.UinPairMsgs == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var delItems []*pb.MessageItem
|
||||
for _, pairMsg := range rsp.UinPairMsgs {
|
||||
for _, message := range pairMsg.Messages {
|
||||
// delete message
|
||||
delItem := &pb.MessageItem{
|
||||
FromUin: message.Head.FromUin,
|
||||
ToUin: message.Head.ToUin,
|
||||
MsgType: 187,
|
||||
MsgSeq: message.Head.MsgSeq,
|
||||
MsgUid: message.Head.MsgUid,
|
||||
}
|
||||
delItems = append(delItems, delItem)
|
||||
|
||||
if message.Head.ToUin != c.Uin {
|
||||
continue
|
||||
}
|
||||
if message.Body.RichText == nil || message.Body.RichText.Elems == nil {
|
||||
continue
|
||||
}
|
||||
if c.lastMessageSeq >= message.Head.MsgSeq {
|
||||
continue
|
||||
}
|
||||
c.lastMessageSeq = message.Head.MsgSeq
|
||||
|
||||
c.dispatchFriendMessage(c.parsePrivateMessage(message))
|
||||
}
|
||||
}
|
||||
_, _ = c.sendAndWait(c.buildDeleteMessageRequestPacket(delItems))
|
||||
if rsp.SyncFlag != msg.SyncFlag_STOP {
|
||||
seq, pkt := c.buildGetMessageRequestPacket(rsp.SyncFlag, time.Now().Unix())
|
||||
_, _ = c.sendAndWait(seq, pkt)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func decodeGroupMessagePacket(c *QQClient, payload []byte) (interface{}, error) {
|
||||
pkt := msg.PushMessagePacket{}
|
||||
err := proto.Unmarshal(payload, &pkt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pkt.Message.Head.FromUin == c.Uin {
|
||||
return nil, nil
|
||||
}
|
||||
c.dispatchGroupMessage(c.parseGroupMessage(pkt.Message))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func decodeSvcNotify(c *QQClient, payload []byte) (interface{}, error) {
|
||||
_, pkt := c.buildGetMessageRequestPacket(msg.SyncFlag_START, time.Now().Unix())
|
||||
return nil, c.send(pkt)
|
||||
}
|
||||
|
||||
func decodeFriendGroupListResponse(c *QQClient, payload []byte) (interface{}, error) {
|
||||
request := &jce.RequestPacket{}
|
||||
request.ReadFrom(jce.NewJceReader(payload))
|
||||
data := &jce.RequestDataVersion3{}
|
||||
data.ReadFrom(jce.NewJceReader(request.SBuffer))
|
||||
r := jce.NewJceReader(data.Map["FLRESP"][1:])
|
||||
totalFriendCount := r.ReadInt16(5)
|
||||
friends := []jce.FriendInfo{}
|
||||
r.ReadSlice(&friends, 7)
|
||||
var l []*FriendInfo
|
||||
for _, f := range friends {
|
||||
l = append(l, &FriendInfo{
|
||||
Uin: f.FriendUin,
|
||||
Nickname: f.Nick,
|
||||
Remark: f.Remark,
|
||||
FaceId: f.FaceId,
|
||||
})
|
||||
}
|
||||
rsp := FriendListResponse{
|
||||
TotalCount: int32(totalFriendCount),
|
||||
List: l,
|
||||
}
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
func decodeGroupListResponse(c *QQClient, payload []byte) (interface{}, error) {
|
||||
request := &jce.RequestPacket{}
|
||||
request.ReadFrom(jce.NewJceReader(payload))
|
||||
data := &jce.RequestDataVersion3{}
|
||||
data.ReadFrom(jce.NewJceReader(request.SBuffer))
|
||||
r := jce.NewJceReader(data.Map["GetTroopListRespV2"][1:])
|
||||
groups := []jce.TroopNumber{}
|
||||
r.ReadSlice(&groups, 5)
|
||||
var l []*GroupInfo
|
||||
for _, g := range groups {
|
||||
l = append(l, &GroupInfo{
|
||||
Uin: g.GroupUin,
|
||||
Code: g.GroupCode,
|
||||
Name: g.GroupName,
|
||||
Memo: g.GroupMemo,
|
||||
OwnerUin: uint32(g.GroupOwnerUin),
|
||||
MemberCount: uint16(g.MemberNum),
|
||||
MaxMemberCount: uint16(g.MaxGroupMemberNum),
|
||||
})
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func decodeGroupMemberListResponse(c *QQClient, payload []byte) (interface{}, error) {
|
||||
request := &jce.RequestPacket{}
|
||||
request.ReadFrom(jce.NewJceReader(payload))
|
||||
data := &jce.RequestDataVersion3{}
|
||||
data.ReadFrom(jce.NewJceReader(request.SBuffer))
|
||||
r := jce.NewJceReader(data.Map["GTMLRESP"][1:])
|
||||
members := []jce.TroopMemberInfo{}
|
||||
r.ReadSlice(&members, 3)
|
||||
next := r.ReadInt64(4)
|
||||
var l []GroupMemberInfo
|
||||
for _, m := range members {
|
||||
l = append(l, GroupMemberInfo{
|
||||
Uin: m.MemberUin,
|
||||
Nickname: m.Nick,
|
||||
CardName: m.Name,
|
||||
Level: uint16(m.MemberLevel),
|
||||
JoinTime: m.JoinTime,
|
||||
LastSpeakTime: m.LastSpeakTime,
|
||||
SpecialTitle: m.SpecialTitle,
|
||||
SpecialTitleExpireTime: m.SpecialTitleExpireTime,
|
||||
Job: m.Job,
|
||||
})
|
||||
}
|
||||
return groupMemberListResponse{
|
||||
NextUin: next,
|
||||
list: l,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func decodeGroupImageStoreResponse(c *QQClient, payload []byte) (interface{}, error) {
|
||||
pkt := pb.D388RespBody{}
|
||||
err := proto.Unmarshal(payload, &pkt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rsp := pkt.MsgTryupImgRsp[0]
|
||||
if rsp.Result != 0 {
|
||||
return groupImageUploadResponse{
|
||||
ResultCode: rsp.Result,
|
||||
Message: rsp.FailMsg,
|
||||
}, nil
|
||||
}
|
||||
if rsp.BoolFileExit {
|
||||
return groupImageUploadResponse{IsExists: true}, nil
|
||||
}
|
||||
return groupImageUploadResponse{
|
||||
UploadKey: rsp.UpUkey,
|
||||
UploadIp: rsp.Uint32UpIp,
|
||||
UploadPort: rsp.Uint32UpPort,
|
||||
}, nil
|
||||
}
|
80
client/entities.go
Normal file
80
client/entities.go
Normal file
@ -0,0 +1,80 @@
|
||||
package client
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrAlreadyRunning = errors.New("already running")
|
||||
)
|
||||
|
||||
type (
|
||||
LoginError int
|
||||
LoginResponse struct {
|
||||
Success bool
|
||||
Error LoginError
|
||||
|
||||
// Captcha info
|
||||
CaptchaImage []byte
|
||||
CaptchaSign []byte
|
||||
|
||||
// other error
|
||||
ErrorMessage string
|
||||
}
|
||||
|
||||
FriendInfo struct {
|
||||
Uin int64
|
||||
Nickname string
|
||||
Remark string
|
||||
FaceId int16
|
||||
}
|
||||
|
||||
FriendListResponse struct {
|
||||
TotalCount int32
|
||||
List []*FriendInfo
|
||||
}
|
||||
|
||||
GroupInfo struct {
|
||||
Uin int64
|
||||
Code int64
|
||||
Name string
|
||||
Memo string
|
||||
OwnerUin uint32
|
||||
MemberCount uint16
|
||||
MaxMemberCount uint16
|
||||
Members []GroupMemberInfo
|
||||
}
|
||||
|
||||
GroupMemberInfo struct {
|
||||
Uin int64
|
||||
Nickname string
|
||||
CardName string
|
||||
Level uint16
|
||||
JoinTime int64
|
||||
LastSpeakTime int64
|
||||
SpecialTitle string
|
||||
SpecialTitleExpireTime int64
|
||||
Job string
|
||||
}
|
||||
|
||||
groupMemberListResponse struct {
|
||||
NextUin int64
|
||||
list []GroupMemberInfo
|
||||
}
|
||||
|
||||
groupImageUploadResponse struct {
|
||||
ResultCode int32
|
||||
Message string
|
||||
|
||||
IsExists bool
|
||||
|
||||
UploadKey []byte
|
||||
UploadIp []int32
|
||||
UploadPort []int32
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
NeedCaptcha LoginError = 1
|
||||
DeviceLockError = 2
|
||||
OtherLoginError = 3
|
||||
UnknownLoginError = -1
|
||||
)
|
57
client/events.go
Normal file
57
client/events.go
Normal file
@ -0,0 +1,57 @@
|
||||
package client
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/message"
|
||||
|
||||
func (c *QQClient) OnPrivateMessage(f func(*QQClient, *message.PrivateMessage)) {
|
||||
c.privateMessageHandlers = append(c.privateMessageHandlers, f)
|
||||
}
|
||||
|
||||
func (c *QQClient) OnPrivateMessageF(filter func(*message.PrivateMessage) bool, f func(*QQClient, *message.PrivateMessage)) {
|
||||
c.privateMessageHandlers = append(c.privateMessageHandlers, func(client *QQClient, msg *message.PrivateMessage) {
|
||||
if filter(msg) {
|
||||
f(client, msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (c *QQClient) OnGroupMessage(f func(*QQClient, *message.GroupMessage)) {
|
||||
c.groupMessageHandlers = append(c.groupMessageHandlers, f)
|
||||
}
|
||||
|
||||
func NewUinFilterPrivate(uin int64) func(*message.PrivateMessage) bool {
|
||||
return func(msg *message.PrivateMessage) bool {
|
||||
return msg.Sender.Uin == uin
|
||||
}
|
||||
}
|
||||
|
||||
func (c *QQClient) dispatchFriendMessage(msg *message.PrivateMessage) {
|
||||
if msg == nil {
|
||||
return
|
||||
}
|
||||
for _, f := range c.privateMessageHandlers {
|
||||
func() {
|
||||
defer func() {
|
||||
if pan := recover(); pan != nil {
|
||||
//
|
||||
}
|
||||
}()
|
||||
f(c, msg)
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *QQClient) dispatchGroupMessage(msg *message.GroupMessage) {
|
||||
if msg == nil {
|
||||
return
|
||||
}
|
||||
for _, f := range c.groupMessageHandlers {
|
||||
func() {
|
||||
defer func() {
|
||||
if pan := recover(); pan != nil {
|
||||
// TODO: logger
|
||||
}
|
||||
}()
|
||||
f(c, msg)
|
||||
}()
|
||||
}
|
||||
}
|
201
client/global.go
Normal file
201
client/global.go
Normal file
@ -0,0 +1,201 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
devinfo "github.com/Mrs4s/MiraiGo/client/pb"
|
||||
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
||||
"github.com/Mrs4s/MiraiGo/message"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
type DeviceInfo struct {
|
||||
Display []byte
|
||||
Product []byte
|
||||
Device []byte
|
||||
Board []byte
|
||||
Brand []byte
|
||||
Model []byte
|
||||
Bootloader []byte
|
||||
FingerPrint []byte
|
||||
BootId []byte
|
||||
ProcVersion []byte
|
||||
BaseBand []byte
|
||||
SimInfo []byte
|
||||
OSType []byte
|
||||
MacAddress []byte
|
||||
IpAddress []byte
|
||||
WifiBSSID []byte
|
||||
WifiSSID []byte
|
||||
IMSIMd5 []byte
|
||||
IMEI string
|
||||
AndroidId []byte
|
||||
APN []byte
|
||||
Guid []byte
|
||||
TgtgtKey []byte
|
||||
Version *Version
|
||||
}
|
||||
|
||||
type Version struct {
|
||||
Incremental []byte
|
||||
Release []byte
|
||||
CodeName []byte
|
||||
Sdk uint32
|
||||
}
|
||||
|
||||
var SystemDeviceInfo = &DeviceInfo{
|
||||
Display: []byte("MIRAI.123456.001"),
|
||||
Product: []byte("mirai"),
|
||||
Device: []byte("mirai"),
|
||||
Board: []byte("mirai"),
|
||||
Brand: []byte("mamoe"),
|
||||
Model: []byte("mirai"),
|
||||
Bootloader: []byte("unknown"),
|
||||
FingerPrint: []byte("mamoe/mirai/mirai:10/MIRAI.200122.001/1234567:user/release-keys"),
|
||||
BootId: []byte("cb886ae2-00b6-4d68-a230-787f111d12c7"),
|
||||
ProcVersion: []byte("Linux version 3.0.31-cb886ae2 (android-build@xxx.xxx.xxx.xxx.com)"),
|
||||
BaseBand: []byte{},
|
||||
SimInfo: []byte("T-Mobile"),
|
||||
OSType: []byte("android"),
|
||||
MacAddress: []byte("00:50:56:C0:00:08"),
|
||||
IpAddress: []byte{10, 0, 1, 3}, // 10.0.1.3
|
||||
WifiBSSID: []byte("00:50:56:C0:00:08"),
|
||||
WifiSSID: []byte("<unknown ssid>"),
|
||||
IMEI: "468356291846738",
|
||||
AndroidId: []byte("MIRAI.123456.001"),
|
||||
APN: []byte("wifi"),
|
||||
Version: &Version{
|
||||
Incremental: []byte("5891938"),
|
||||
Release: []byte("10"),
|
||||
CodeName: []byte("REL"),
|
||||
Sdk: 29,
|
||||
},
|
||||
}
|
||||
|
||||
var EmptyBytes = []byte{}
|
||||
|
||||
func init() {
|
||||
r := make([]byte, 16)
|
||||
rand.Read(r)
|
||||
t := md5.Sum(r)
|
||||
SystemDeviceInfo.IMSIMd5 = t[:]
|
||||
SystemDeviceInfo.GenNewGuid()
|
||||
SystemDeviceInfo.GenNewTgtgtKey()
|
||||
}
|
||||
|
||||
func (info *DeviceInfo) GenNewGuid() {
|
||||
t := md5.Sum(append(info.AndroidId, info.MacAddress...))
|
||||
info.Guid = t[:]
|
||||
}
|
||||
|
||||
func (info *DeviceInfo) GenNewTgtgtKey() {
|
||||
r := make([]byte, 16)
|
||||
rand.Read(r)
|
||||
t := md5.Sum(append(r, info.Guid...))
|
||||
info.TgtgtKey = t[:]
|
||||
}
|
||||
|
||||
func (info *DeviceInfo) GenDeviceInfoData() []byte {
|
||||
msg := &devinfo.DeviceInfo{
|
||||
Bootloader: string(info.Bootloader),
|
||||
ProcVersion: string(info.ProcVersion),
|
||||
Codename: string(info.Version.CodeName),
|
||||
Incremental: string(info.Version.Incremental),
|
||||
Fingerprint: string(info.FingerPrint),
|
||||
BootId: string(info.BootId),
|
||||
AndroidId: string(info.AndroidId),
|
||||
BaseBand: string(info.BaseBand),
|
||||
InnerVersion: string(info.Version.Incremental),
|
||||
}
|
||||
data, err := proto.Marshal(msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (c *QQClient) parsePrivateMessage(msg *msg.Message) *message.PrivateMessage {
|
||||
switch msg.Head.MsgType {
|
||||
case 166:
|
||||
friend := c.FindFriend(msg.Head.FromUin)
|
||||
if friend == nil {
|
||||
return nil
|
||||
}
|
||||
return &message.PrivateMessage{
|
||||
Id: msg.Head.MsgSeq,
|
||||
Sender: &message.Sender{
|
||||
Uin: friend.Uin,
|
||||
Nickname: friend.Nickname,
|
||||
},
|
||||
Elements: parseMessageElems(msg.Body.RichText.Elems),
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
|
||||
group := c.FindGroup(m.Head.GroupInfo.GroupCode)
|
||||
if group == nil {
|
||||
return nil
|
||||
}
|
||||
var anonInfo *msg.AnonymousGroupMessage
|
||||
for _, e := range m.Body.RichText.Elems {
|
||||
if e.AnonGroupMsg != nil {
|
||||
anonInfo = e.AnonGroupMsg
|
||||
}
|
||||
}
|
||||
var sender *message.Sender
|
||||
if anonInfo != nil {
|
||||
sender = &message.Sender{
|
||||
Uin: 80000000,
|
||||
Nickname: string(anonInfo.AnonNick),
|
||||
IsFriend: false,
|
||||
}
|
||||
} else {
|
||||
mem := group.FindMember(m.Head.FromUin)
|
||||
if mem == nil {
|
||||
return nil
|
||||
}
|
||||
sender = &message.Sender{
|
||||
Uin: mem.Uin,
|
||||
Nickname: mem.Nickname,
|
||||
CardName: mem.CardName,
|
||||
IsFriend: c.FindFriend(mem.Uin) != nil,
|
||||
}
|
||||
}
|
||||
g := &message.GroupMessage{
|
||||
Id: m.Head.MsgSeq,
|
||||
GroupUin: m.Head.GroupInfo.GroupCode,
|
||||
GroupName: string(m.Head.GroupInfo.GroupName),
|
||||
Sender: sender,
|
||||
Elements: parseMessageElems(m.Body.RichText.Elems),
|
||||
IsAdministrator: false,
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
func parseMessageElems(elems []*msg.Elem) []message.IMessageElement {
|
||||
var res []message.IMessageElement
|
||||
for _, elem := range elems {
|
||||
if elem.Text != nil {
|
||||
res = append(res, message.NewText(elem.Text.Str))
|
||||
}
|
||||
if elem.CustomFace != nil {
|
||||
res = append(res, message.NewNetImage(elem.CustomFace.FilePath, "http://gchat.qpic.cn/"+elem.CustomFace.OrigUrl))
|
||||
}
|
||||
if elem.NotOnlineImage != nil {
|
||||
var img string
|
||||
if elem.NotOnlineImage.OrigUrl != "" {
|
||||
img = "http://c2cpicdw.qpic.cn" + elem.NotOnlineImage.OrigUrl
|
||||
} else {
|
||||
img = "http://c2cpicdw.qpic.cn/offpic_new/0/" + elem.NotOnlineImage.ResId + "/0?term=2"
|
||||
}
|
||||
res = append(res, message.NewNetImage(elem.NotOnlineImage.FilePath, img))
|
||||
}
|
||||
if elem.Face != nil {
|
||||
res = append(res, message.NewFace(elem.Face.Index))
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
1959
client/pb/data.pb.go
Normal file
1959
client/pb/data.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
157
client/pb/data.proto
Normal file
157
client/pb/data.proto
Normal file
@ -0,0 +1,157 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option go_package = ".;pb";
|
||||
|
||||
message DeviceInfo {
|
||||
string bootloader = 1;
|
||||
string procVersion = 2;
|
||||
string codename = 3;
|
||||
string incremental = 4;
|
||||
string fingerprint = 5;
|
||||
string bootId = 6;
|
||||
string androidId = 7;
|
||||
string baseBand = 8;
|
||||
string innerVersion = 9;
|
||||
}
|
||||
|
||||
message RequestBody {
|
||||
repeated ConfigSeq rpt_config_list = 1;
|
||||
}
|
||||
|
||||
message ConfigSeq {
|
||||
int32 type = 1;
|
||||
int32 version = 2;
|
||||
}
|
||||
|
||||
message D50ReqBody {
|
||||
int64 appid = 1;
|
||||
int32 maxPkgSize = 2;
|
||||
int32 startTime = 3;
|
||||
int32 startIndex = 4;
|
||||
int32 reqNum = 5;
|
||||
repeated int64 uinList = 6;
|
||||
int32 reqMusicSwitch = 91001;
|
||||
int32 reqMutualmarkAlienation = 101001;
|
||||
int32 reqMutualmarkScore = 141001;
|
||||
int32 reqKsingSwitch = 151001;
|
||||
int32 reqMutualmarkLbsshare = 181001;
|
||||
}
|
||||
|
||||
message D388ReqBody {
|
||||
int32 netType = 1;
|
||||
int32 subcmd = 2;
|
||||
repeated TryUpImgReq msgTryupImgReq = 3;
|
||||
int32 commandId = 7;
|
||||
bytes extension = 1001;
|
||||
}
|
||||
|
||||
message D388RespBody {
|
||||
int32 clientIp = 1;
|
||||
int32 subCmd = 2;
|
||||
repeated TryUpImgResp msgTryupImgRsp = 3;
|
||||
}
|
||||
|
||||
message ReqDataHighwayHead {
|
||||
DataHighwayHead msgBasehead = 1;
|
||||
SegHead msgSeghead = 2;
|
||||
bytes reqExtendinfo = 3;
|
||||
int64 timestamp = 4;
|
||||
//LoginSigHead? msgLoginSigHead = 5;
|
||||
}
|
||||
|
||||
message RspDataHighwayHead {
|
||||
DataHighwayHead msgBasehead = 1;
|
||||
SegHead msgSeghead = 2;
|
||||
int32 errorCode = 3;
|
||||
int32 allowRetry = 4;
|
||||
int32 cachecost = 5;
|
||||
int32 htcost = 6;
|
||||
bytes rspExtendinfo = 7;
|
||||
int64 timestamp = 8;
|
||||
int64 range = 9;
|
||||
int32 isReset = 10;
|
||||
}
|
||||
|
||||
message DataHighwayHead {
|
||||
int32 version = 1;
|
||||
string uin = 2;
|
||||
string command = 3;
|
||||
int32 seq = 4;
|
||||
int32 retryTimes = 5;
|
||||
int32 appid = 6;
|
||||
int32 dataflag = 7;
|
||||
int32 commandId = 8;
|
||||
string buildVer = 9;
|
||||
int32 localeId = 10;
|
||||
}
|
||||
|
||||
message SegHead {
|
||||
int32 serviceid = 1;
|
||||
int64 filesize = 2;
|
||||
int64 dataoffset = 3;
|
||||
int32 datalength = 4;
|
||||
int32 rtcode = 5;
|
||||
bytes serviceticket = 6;
|
||||
int32 flag = 7;
|
||||
bytes md5 = 8;
|
||||
bytes fileMd5 = 9;
|
||||
int32 cacheAddr = 10;
|
||||
int32 queryTimes = 11;
|
||||
int32 updateCacheip = 12;
|
||||
}
|
||||
|
||||
message TryUpImgReq {
|
||||
int64 groupCode = 1;
|
||||
int64 srcUin = 2;
|
||||
int64 fileId = 3;
|
||||
bytes fileMd5 = 4;
|
||||
int64 fileSize = 5;
|
||||
string fileName = 6;
|
||||
int32 srcTerm = 7;
|
||||
int32 platformType = 8;
|
||||
int32 buType = 9;
|
||||
int32 picWidth = 10;
|
||||
int32 picHeight = 11;
|
||||
int32 picType = 12;
|
||||
string buildVer = 13;
|
||||
int32 innerIp = 14;
|
||||
int32 appPicType = 15;
|
||||
int32 originalPic = 16;
|
||||
bytes fileIndex = 17;
|
||||
int64 dstUin = 18;
|
||||
int32 srvUpload = 19;
|
||||
bytes transferUrl = 20;
|
||||
}
|
||||
|
||||
message TryUpImgResp {
|
||||
int64 fileId = 1;
|
||||
int32 result = 2;
|
||||
string failMsg = 3;
|
||||
bool boolFileExit = 4;
|
||||
ImgInfo msgImgInfo = 5;
|
||||
repeated int32 uint32UpIp = 6;
|
||||
repeated int32 uint32UpPort = 7;
|
||||
bytes upUkey = 8;
|
||||
int64 fid = 9;
|
||||
}
|
||||
|
||||
message ImgInfo {
|
||||
bytes fileMd5 = 1;
|
||||
int32 fileType = 2;
|
||||
int64 fileSize = 3;
|
||||
int32 fileWidth = 4;
|
||||
int32 fileHeight = 5;
|
||||
}
|
||||
|
||||
message DeleteMessageRequest {
|
||||
repeated MessageItem items = 1;
|
||||
}
|
||||
|
||||
message MessageItem {
|
||||
int64 fromUin = 1;
|
||||
int64 toUin = 2;
|
||||
int32 msgType = 3;
|
||||
int32 msgSeq = 4;
|
||||
int64 msgUid = 5;
|
||||
bytes sig = 7;
|
||||
}
|
5402
client/pb/msg/msg.pb.go
Normal file
5402
client/pb/msg/msg.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
519
client/pb/msg/msg.proto
Normal file
519
client/pb/msg/msg.proto
Normal file
@ -0,0 +1,519 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option go_package = ".;msg";
|
||||
|
||||
message GetMessageRequest {
|
||||
SyncFlag syncFlag = 1;
|
||||
bytes syncCookie = 2;
|
||||
int32 rambleFlag = 3;
|
||||
int32 latestRambleNumber = 4;
|
||||
int32 otherRambleNumber = 5;
|
||||
int32 onlineSyncFlag = 6;
|
||||
int32 contextFlag = 7;
|
||||
int32 whisperSessionId = 8;
|
||||
int32 msgReqType = 9;
|
||||
bytes pubaccountCookie = 10;
|
||||
bytes msgCtrlBuf = 11;
|
||||
bytes serverBuf = 12;
|
||||
}
|
||||
|
||||
message SendMessageRequest {
|
||||
RoutingHead routingHead = 1;
|
||||
ContentHead contentHead = 2;
|
||||
MessageBody msgBody = 3;
|
||||
int32 msgSeq = 4;
|
||||
int32 msgRand = 5;
|
||||
bytes syncCookie = 6;
|
||||
//MsgComm.AppShareInfo? appShare = 7;
|
||||
int32 msgVia = 8;
|
||||
int32 dataStatist = 9;
|
||||
//MultiMsgAssist? multiMsgAssist = 10;
|
||||
//PbInputNotifyInfo? inputNotifyInfo = 11;
|
||||
MsgCtrl msgCtrl = 12;
|
||||
//ImReceipt.ReceiptReq? receiptReq = 13;
|
||||
int32 multiSendSeq = 14;
|
||||
|
||||
}
|
||||
|
||||
message RoutingHead {
|
||||
C2C c2c = 1;
|
||||
Grp grp = 2;
|
||||
GrpTmp grpTmp = 3;
|
||||
/*
|
||||
Dis dis = 4;
|
||||
DisTmp disTmp = 5;
|
||||
WPATmp? wpaTmp = 6;
|
||||
SecretFileHead? secretFile = 7;
|
||||
PublicPlat? publicPlat = 8;
|
||||
TransMsg? transMsg = 9;
|
||||
AddressListTmp? addressList = 10;
|
||||
RichStatusTmp? richStatusTmp = 11;
|
||||
TransCmd? transCmd = 12;
|
||||
AccostTmp? accostTmp = 13;
|
||||
PubGroupTmp? pubGroupTmp = 14;
|
||||
Trans0x211? trans0x211 = 15;
|
||||
BusinessWPATmp? businessWpaTmp = 16;
|
||||
AuthTmp? authTmp = 17;
|
||||
BsnsTmp? bsnsTmp = 18;
|
||||
QQQueryBusinessTmp? qqQuerybusinessTmp = 19;
|
||||
NearByDatingTmp? nearbyDatingTmp = 20;
|
||||
NearByAssistantTmp? nearbyAssistantTmp = 21;
|
||||
CommTmp? commTmp = 22;
|
||||
*/
|
||||
}
|
||||
|
||||
message C2C {
|
||||
int64 toUin = 1;
|
||||
}
|
||||
|
||||
message Grp {
|
||||
int64 groupCode = 1;
|
||||
}
|
||||
|
||||
message GrpTmp {
|
||||
int64 groupUin = 1;
|
||||
int64 toUin = 2;
|
||||
}
|
||||
|
||||
message MsgCtrl {
|
||||
int32 msgFlag = 1;
|
||||
}
|
||||
|
||||
message GetMessageResponse {
|
||||
int32 result = 1;
|
||||
string errorMessage = 2;
|
||||
bytes syncCookie = 3;
|
||||
SyncFlag syncFlag = 4;
|
||||
repeated UinPairMessage uinPairMsgs = 5;
|
||||
int64 bindUin = 6;
|
||||
int32 msgRspType = 7;
|
||||
bytes pubAccountCookie = 8;
|
||||
bool isPartialSync = 9;
|
||||
bytes msgCtrlBuf = 10;
|
||||
}
|
||||
|
||||
message PushMessagePacket {
|
||||
Message message = 1;
|
||||
int32 svrip = 2;
|
||||
bytes pushToken = 3;
|
||||
int32 pingFLag = 4;
|
||||
int32 generalFlag = 9;
|
||||
}
|
||||
|
||||
message UinPairMessage {
|
||||
int32 lastReadTime = 1;
|
||||
int64 peerUin = 2;
|
||||
int32 msgCompleted = 3;
|
||||
repeated Message messages = 4;
|
||||
}
|
||||
|
||||
message Message {
|
||||
MessageHead head = 1;
|
||||
MessageBody body = 3;
|
||||
}
|
||||
|
||||
message MessageBody {
|
||||
RichText richText = 1;
|
||||
bytes msgContent = 2;
|
||||
bytes msgEncryptContent = 3;
|
||||
}
|
||||
|
||||
message RichText {
|
||||
Attr attr = 1;
|
||||
repeated Elem elems = 2;
|
||||
NotOnlineFile notOnlineFile = 3;
|
||||
Ptt ptt = 4;
|
||||
}
|
||||
|
||||
message Elem {
|
||||
Text text = 1;
|
||||
Face face = 2;
|
||||
OnlineImage onlineImage = 3;
|
||||
NotOnlineImage notOnlineImage = 4;
|
||||
//TransElem transElemInfo = 5;
|
||||
//MarketFace marketFace = 6;
|
||||
//ElemFlags elemFlags = 7;
|
||||
CustomFace customFace = 8;
|
||||
//ElemFlags2 elemFlags2 = 9;
|
||||
//FunFace funFace = 10;
|
||||
//SecretFileMsg secretFile = 11;
|
||||
//RichMsg richMsg = 12;
|
||||
GroupFile groupFile = 13;
|
||||
//PubGroup pubGroup = 14;
|
||||
//MarketTrans marketTrans = 15;
|
||||
ExtraInfo extraInfo = 16;
|
||||
//ShakeWindow? shakeWindow = 17;
|
||||
//PubAccount? pubAccount = 18;
|
||||
VideoFile videoFile = 19;
|
||||
//TipsInfo? tipsInfo = 20;
|
||||
AnonymousGroupMessage anonGroupMsg = 21;
|
||||
//QQLiveOld? qqLiveOld = 22;
|
||||
//LifeOnlineAccount? lifeOnline = 23;
|
||||
//QQWalletMsg? qqwalletMsg = 24;
|
||||
//CrmElem? crmElem = 25;
|
||||
//ConferenceTipsInfo? conferenceTipsInfo = 26;
|
||||
//RedBagInfo? redbagInfo = 27;
|
||||
//LowVersionTips? lowVersionTips = 28;
|
||||
//bytes bankcodeCtrlInfo = 29;
|
||||
//NearByMessageType? nearByMsg = 30;
|
||||
CustomElem customElem = 31;
|
||||
//LocationInfo? locationInfo = 32;
|
||||
//PubAccInfo? pubAccInfo = 33;
|
||||
//SmallEmoji? smallEmoji = 34;
|
||||
//FSJMessageElem? fsjMsgElem = 35;
|
||||
//ArkAppElem? arkApp = 36;
|
||||
//GeneralFlags? generalFlags = 37;
|
||||
//CustomFace? hcFlashPic = 38;
|
||||
//DeliverGiftMsg? deliverGiftMsg = 39;
|
||||
//BitAppMsg? bitappMsg = 40;
|
||||
//OpenQQData? openQqData = 41;
|
||||
//ApolloActMsg? apolloMsg = 42;
|
||||
//GroupPubAccountInfo? groupPubAccInfo = 43;
|
||||
//BlessingMessage? blessMsg = 44;
|
||||
//SourceMsg? srcMsg = 45;
|
||||
//LolaMsg? lolaMsg = 46;
|
||||
//GroupBusinessMsg? groupBusinessMsg = 47;
|
||||
//WorkflowNotifyMsg? msgWorkflowNotify = 48;
|
||||
//PatsElem? patElem = 49;
|
||||
//GroupPostElem? groupPostElem = 50;
|
||||
//LightAppElem? lightApp = 51;
|
||||
//EIMInfo? eimInfo = 52;
|
||||
//CommonElem? commonElem = 53;
|
||||
}
|
||||
|
||||
message CustomElem {
|
||||
bytes desc = 1;
|
||||
bytes data = 2;
|
||||
int32 enumType = 3;
|
||||
bytes ext = 4;
|
||||
bytes sound = 5;
|
||||
}
|
||||
|
||||
message Text {
|
||||
string str = 1;
|
||||
string link = 2;
|
||||
bytes attr6Buf = 3;
|
||||
bytes attr7Buf = 4;
|
||||
bytes buf = 11;
|
||||
bytes pbReserve = 12;
|
||||
}
|
||||
|
||||
message Attr {
|
||||
int32 codePage = 1;
|
||||
int32 time = 2;
|
||||
int32 random = 3;
|
||||
int32 color = 4;
|
||||
int32 size = 5;
|
||||
int32 effect = 6;
|
||||
int32 charSet = 7;
|
||||
int32 pitchAndFamily = 8;
|
||||
string fontName = 9;
|
||||
bytes reserveData = 10;
|
||||
}
|
||||
|
||||
message Ptt {
|
||||
int32 fileType = 1;
|
||||
int64 srcUin = 2;
|
||||
bytes fileUuid = 3;
|
||||
bytes fileMd5 = 4;
|
||||
bytes fileName = 5;
|
||||
int32 fileSize = 6;
|
||||
bytes reserve = 7;
|
||||
int32 fileId = 8;
|
||||
int32 serverIp = 9;
|
||||
int32 serverPort = 10;
|
||||
bool boolValid = 11;
|
||||
bytes signature = 12;
|
||||
bytes shortcut = 13;
|
||||
bytes fileKey = 14;
|
||||
int32 magicPttIndex = 15;
|
||||
int32 voiceSwitch = 16;
|
||||
bytes pttUrl = 17;
|
||||
bytes groupFileKey = 18;
|
||||
int32 time = 19;
|
||||
bytes downPara = 20;
|
||||
int32 format = 29;
|
||||
bytes pbReserve = 30;
|
||||
repeated bytes bytesPttUrls = 31;
|
||||
int32 downloadFlag = 32;
|
||||
|
||||
}
|
||||
|
||||
message OnlineImage {
|
||||
bytes guid = 1;
|
||||
bytes filePath = 2;
|
||||
bytes oldVerSendFile = 3;
|
||||
}
|
||||
|
||||
message NotOnlineImage {
|
||||
string filePath = 1;
|
||||
int32 fileLen = 2;
|
||||
string downloadPath = 3;
|
||||
bytes oldVerSendFile = 4;
|
||||
int32 imgType = 5;
|
||||
bytes previewsImage = 6;
|
||||
bytes picMd5 = 7;
|
||||
int32 picHeight = 8;
|
||||
int32 picWidth = 9;
|
||||
string resId = 10;
|
||||
bytes flag = 11;
|
||||
string thumbUrl = 12;
|
||||
int32 original = 13;
|
||||
string bigUrl = 14;
|
||||
string origUrl = 15;
|
||||
int32 bizType = 16;
|
||||
int32 result = 17;
|
||||
int32 index = 18;
|
||||
bytes opFaceBuf = 19;
|
||||
bool oldPicMd5 = 20;
|
||||
int32 thumbWidth = 21;
|
||||
int32 thumbHeight = 22;
|
||||
int32 fileId = 23;
|
||||
int32 showLen = 24;
|
||||
int32 downloadLen = 25;
|
||||
bytes pbReserve = 29;
|
||||
}
|
||||
|
||||
message NotOnlineFile {
|
||||
int32 fileType = 1;
|
||||
bytes sig = 2;
|
||||
bytes fileUuid = 3;
|
||||
bytes fileMd5 = 4;
|
||||
bytes fileName = 5;
|
||||
int64 fileSize = 6;
|
||||
bytes note = 7;
|
||||
int32 reserved = 8;
|
||||
int32 subcmd = 9;
|
||||
int32 microCloud = 10;
|
||||
repeated bytes bytesFileUrls = 11;
|
||||
int32 downloadFlag = 12;
|
||||
int32 dangerEvel = 50;
|
||||
int32 lifeTime = 51;
|
||||
int32 uploadTime = 52;
|
||||
int32 absFileType = 53;
|
||||
int32 clientType = 54;
|
||||
int32 expireTime = 55;
|
||||
bytes pbReserve = 56;
|
||||
}
|
||||
|
||||
message ExtraInfo {
|
||||
bytes nick = 1;
|
||||
bytes groupCard = 2;
|
||||
int32 level = 3;
|
||||
int32 flags = 4;
|
||||
int32 groupMask = 5;
|
||||
int32 msgTailId = 6;
|
||||
bytes senderTitle = 7;
|
||||
bytes apnsTips = 8;
|
||||
int64 uin = 9;
|
||||
int32 msgStateFlag = 10;
|
||||
int32 apnsSoundType = 11;
|
||||
int32 newGroupFlag = 12;
|
||||
}
|
||||
|
||||
message GroupFile {
|
||||
bytes filename = 1;
|
||||
int64 fileSize = 2;
|
||||
bytes fileId = 3;
|
||||
bytes batchId = 4;
|
||||
bytes fileKey = 5;
|
||||
bytes mark = 6;
|
||||
int64 sequence = 7;
|
||||
bytes batchItemId = 8;
|
||||
int32 feedMsgTime = 9;
|
||||
bytes pbReserve = 10;
|
||||
}
|
||||
|
||||
message AnonymousGroupMessage {
|
||||
int32 flags = 1;
|
||||
bytes anonId = 2;
|
||||
bytes anonNick = 3;
|
||||
int32 headPortrait = 4;
|
||||
int32 expireTime = 5;
|
||||
int32 bubbleId = 6;
|
||||
bytes rankColor = 7;
|
||||
}
|
||||
|
||||
message VideoFile {
|
||||
bytes fileUuid = 1;
|
||||
bytes fileMd5 = 2;
|
||||
bytes fileName = 3;
|
||||
int32 fileFormat = 4;
|
||||
int32 fileTime = 5;
|
||||
int32 fileSize = 6;
|
||||
int32 thumbWidth = 7;
|
||||
int32 thumbHeight = 8;
|
||||
bytes thumbFileMd5 = 9;
|
||||
bytes source = 10;
|
||||
int32 thumbFileSize = 11;
|
||||
int32 busiType = 12;
|
||||
int32 fromChatType = 13;
|
||||
int32 toChatType = 14;
|
||||
bool boolSupportProgressive = 15;
|
||||
int32 fileWidth = 16;
|
||||
int32 fileHeight = 17;
|
||||
int32 subBusiType = 18;
|
||||
int32 videoAttr = 19;
|
||||
repeated bytes bytesThumbFileUrls = 20;
|
||||
repeated bytes bytesVideoFileUrls = 21;
|
||||
int32 thumbDownloadFlag = 22;
|
||||
int32 videoDownloadFlag = 23;
|
||||
bytes pbReserve = 24;
|
||||
}
|
||||
|
||||
message Face {
|
||||
int32 index = 1;
|
||||
bytes old = 2;
|
||||
bytes buf = 11;
|
||||
}
|
||||
|
||||
message CustomFace {
|
||||
bytes guid = 1;
|
||||
string filePath = 2;
|
||||
string shortcut = 3;
|
||||
bytes buffer = 4;
|
||||
bytes flag = 5;
|
||||
bytes oldData = 6;
|
||||
int32 fileId = 7;
|
||||
int32 serverIp = 8;
|
||||
int32 serverPort = 9;
|
||||
int32 fileType = 10;
|
||||
bytes signature = 11;
|
||||
int32 useful = 12;
|
||||
bytes md5 = 13;
|
||||
string thumbUrl = 14;
|
||||
string bigUrl = 15;
|
||||
string origUrl = 16;
|
||||
int32 bizType = 17;
|
||||
int32 repeatIndex = 18;
|
||||
int32 repeatImage = 19;
|
||||
int32 imageType = 20;
|
||||
int32 index = 21;
|
||||
int32 width = 22;
|
||||
int32 height = 23;
|
||||
int32 source = 24;
|
||||
int32 size = 25;
|
||||
int32 origin = 26;
|
||||
int32 thumbWidth = 27;
|
||||
int32 thumbHeight = 28;
|
||||
int32 showLen = 29;
|
||||
int32 downloadLen = 30;
|
||||
string _400Url = 31;
|
||||
int32 _400Width = 32;
|
||||
int32 _400Height = 33;
|
||||
bytes pbReserve = 34;
|
||||
}
|
||||
|
||||
message ContentHead {
|
||||
int32 pkgNum = 1;
|
||||
int32 pkgIndex = 2;
|
||||
int32 divSeq = 3;
|
||||
int32 autoReply = 4;
|
||||
}
|
||||
|
||||
message MessageHead {
|
||||
int64 fromUin = 1;
|
||||
int64 toUin = 2;
|
||||
int32 msgType = 3;
|
||||
int32 c2cCmd = 4;
|
||||
int32 msgSeq = 5;
|
||||
int32 msgTime = 6;
|
||||
int64 msgUid = 7;
|
||||
C2CTempMessageHead c2cTmpMsgHead = 8;
|
||||
GroupInfo groupInfo = 9;
|
||||
int32 fromAppid = 10;
|
||||
int32 fromInstid = 11;
|
||||
int32 userActive = 12;
|
||||
DiscussInfo discussInfo = 13;
|
||||
string fromNick = 14;
|
||||
int64 authUin = 15;
|
||||
string authNick = 16;
|
||||
int32 msgFlag = 17;
|
||||
string authRemark = 18;
|
||||
string groupName = 19;
|
||||
MutilTransHead mutiltransHead = 20;
|
||||
InstCtrl msgInstCtrl = 21;
|
||||
int32 publicAccountGroupSendFlag = 22;
|
||||
int32 wseqInC2cMsghead = 23;
|
||||
int64 cpid = 24;
|
||||
ExtGroupKeyInfo extGroupKeyInfo = 25;
|
||||
string multiCompatibleText = 26;
|
||||
int32 authSex = 27;
|
||||
bool isSrcMsg = 28;
|
||||
|
||||
}
|
||||
|
||||
message GroupInfo {
|
||||
int64 groupCode = 1;
|
||||
int32 groupType = 2;
|
||||
int64 groupInfoSeq = 3;
|
||||
string groupCard = 4;
|
||||
bytes groupRank = 5;
|
||||
int32 groupLevel = 6;
|
||||
int32 groupCardType = 7;
|
||||
bytes groupName = 8;
|
||||
}
|
||||
|
||||
message DiscussInfo {
|
||||
int64 discussUin = 1;
|
||||
int32 discussType = 2;
|
||||
int64 discussInfoSeq = 3;
|
||||
bytes discussRemark = 4;
|
||||
bytes discussName = 5;
|
||||
}
|
||||
|
||||
message MutilTransHead{
|
||||
int32 status = 1;
|
||||
int32 msgId = 2;
|
||||
}
|
||||
|
||||
message C2CTempMessageHead {
|
||||
int32 c2cType = 1;
|
||||
int32 serviceType = 2;
|
||||
int64 groupUin = 3;
|
||||
int64 groupCode = 4;
|
||||
bytes sig = 5;
|
||||
int32 sigType = 6;
|
||||
string fromPhone = 7;
|
||||
string toPhone = 8;
|
||||
int32 lockDisplay = 9;
|
||||
int32 directionFlag = 10;
|
||||
bytes reserved = 11;
|
||||
}
|
||||
|
||||
message InstCtrl {
|
||||
repeated InstInfo msgSendToInst = 1;
|
||||
repeated InstInfo msgExcludeInst = 2;
|
||||
InstInfo msgFromInst = 3;
|
||||
|
||||
}
|
||||
|
||||
message InstInfo {
|
||||
int32 apppid = 1;
|
||||
int32 instid = 2;
|
||||
int32 platform = 3;
|
||||
int32 enumDeviceType = 10;
|
||||
}
|
||||
|
||||
message ExtGroupKeyInfo {
|
||||
int32 curMaxSeq = 1;
|
||||
int64 curTime = 2;
|
||||
}
|
||||
|
||||
message SyncCookie {
|
||||
int64 time1 = 1;
|
||||
int64 time = 2;
|
||||
int64 ran1 = 3;
|
||||
int64 ran2 = 4;
|
||||
int64 const1 = 5;
|
||||
int64 const2 = 11;
|
||||
int64 const3 = 12;
|
||||
int64 lastSyncTime = 13;
|
||||
int64 const4 = 14;
|
||||
}
|
||||
|
||||
enum SyncFlag {
|
||||
START = 0;
|
||||
CONTINUME = 1;
|
||||
STOP = 2;
|
||||
}
|
153
client/tlv_decoders.go
Normal file
153
client/tlv_decoders.go
Normal file
@ -0,0 +1,153 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
"time"
|
||||
)
|
||||
|
||||
// --- tlv decoders for qq client ---
|
||||
|
||||
func (c *QQClient) decodeT161(data []byte) {
|
||||
reader := binary.NewReader(data)
|
||||
reader.ReadBytes(2)
|
||||
t := reader.ReadTlvMap(2)
|
||||
if t172, ok := t[0x172]; ok {
|
||||
c.rollbackSig = t172
|
||||
}
|
||||
}
|
||||
|
||||
func (c *QQClient) decodeT119(data []byte) {
|
||||
tea := binary.NewTeaCipher(SystemDeviceInfo.TgtgtKey)
|
||||
reader := binary.NewReader(tea.Decrypt(data))
|
||||
reader.ReadBytes(2)
|
||||
m := reader.ReadTlvMap(2)
|
||||
|
||||
if t130, ok := m[0x130]; ok {
|
||||
c.decodeT130(t130)
|
||||
}
|
||||
if t113, ok := m[0x113]; ok {
|
||||
c.decodeT113(t113)
|
||||
}
|
||||
if t528, ok := m[0x528]; ok {
|
||||
c.t528 = t528
|
||||
}
|
||||
if t530, ok := m[0x530]; ok {
|
||||
c.t530 = t530
|
||||
}
|
||||
if t108, ok := m[0x108]; ok {
|
||||
c.ksid = t108
|
||||
}
|
||||
|
||||
var (
|
||||
//openId []byte
|
||||
//openKey []byte
|
||||
//payToken []byte
|
||||
//pf []byte
|
||||
//pfkey []byte
|
||||
gender uint16 = 0
|
||||
age uint16 = 0
|
||||
nick = ""
|
||||
//a1 []byte
|
||||
//noPicSig []byte
|
||||
//ctime = time.Now().Unix()
|
||||
//etime = ctime + 2160000
|
||||
)
|
||||
|
||||
if _, ok := m[0x125]; ok {
|
||||
//openId, openKey = readT125(t125)
|
||||
}
|
||||
if t186, ok := m[0x186]; ok {
|
||||
c.decodeT186(t186)
|
||||
}
|
||||
if t11a, ok := m[0x11a]; ok {
|
||||
nick, age, gender = readT11A(t11a)
|
||||
}
|
||||
if _, ok := m[0x199]; ok {
|
||||
//openId, payToken = readT199(t199)
|
||||
}
|
||||
if _, ok := m[0x200]; ok {
|
||||
//pf, pfkey = readT200(t200)
|
||||
}
|
||||
if _, ok := m[0x512]; ok {
|
||||
|
||||
} // 暂不处理, Http api cookie
|
||||
if _, ok := m[0x531]; ok {
|
||||
//a1, noPicSig = readT531(t531)
|
||||
}
|
||||
|
||||
c.sigInfo = &loginSigInfo{
|
||||
loginBitmap: 0,
|
||||
tgt: m[0x10a],
|
||||
tgtKey: m[0x10d],
|
||||
userStKey: m[0x10e],
|
||||
userStWebSig: m[0x103],
|
||||
sKey: m[0x120],
|
||||
d2: m[0x143],
|
||||
d2Key: m[0x305],
|
||||
wtSessionTicketKey: m[0x134],
|
||||
deviceToken: m[0x322],
|
||||
}
|
||||
c.Nickname = nick
|
||||
c.Age = age
|
||||
c.Gender = gender
|
||||
}
|
||||
|
||||
func (c *QQClient) decodeT130(data []byte) {
|
||||
reader := binary.NewReader(data)
|
||||
reader.ReadBytes(2)
|
||||
c.timeDiff = int64(reader.ReadInt32()) - time.Now().Unix()
|
||||
c.t149 = reader.ReadBytes(4)
|
||||
}
|
||||
|
||||
func (c *QQClient) decodeT113(data []byte) {
|
||||
reader := binary.NewReader(data)
|
||||
uin := reader.ReadInt32() // ?
|
||||
fmt.Println("got t113 uin:", uin)
|
||||
}
|
||||
|
||||
func (c *QQClient) decodeT186(data []byte) {
|
||||
c.pwdFlag = data[1] == 1
|
||||
}
|
||||
|
||||
// --- tlv readers ---
|
||||
|
||||
func readT125(data []byte) (openId, openKey []byte) {
|
||||
reader := binary.NewReader(data)
|
||||
openId = reader.ReadBytesShort()
|
||||
openKey = reader.ReadBytesShort()
|
||||
return
|
||||
}
|
||||
|
||||
func readT11A(data []byte) (nick string, age, gender uint16) {
|
||||
reader := binary.NewReader(data)
|
||||
reader.ReadUInt16()
|
||||
age = reader.ReadUInt16()
|
||||
gender = uint16(reader.ReadByte())
|
||||
nick = reader.ReadStringLimit(int(reader.ReadByte()) & 0xff)
|
||||
return
|
||||
}
|
||||
|
||||
func readT199(data []byte) (openId, payToken []byte) {
|
||||
reader := binary.NewReader(data)
|
||||
openId = reader.ReadBytesShort()
|
||||
payToken = reader.ReadBytesShort()
|
||||
return
|
||||
}
|
||||
|
||||
func readT200(data []byte) (pf, pfKey []byte) {
|
||||
reader := binary.NewReader(data)
|
||||
pf = reader.ReadBytesShort()
|
||||
pfKey = reader.ReadBytesShort()
|
||||
return
|
||||
}
|
||||
|
||||
func readT531(data []byte) (a1, noPicSig []byte) {
|
||||
reader := binary.NewReader(data)
|
||||
m := reader.ReadTlvMap(2)
|
||||
if m.Exists(0x103) && m.Exists(0x16a) && m.Exists(0x113) && m.Exists(0x10c) {
|
||||
a1 = append(m[0x106], m[0x10c]...)
|
||||
noPicSig = m[0x16a]
|
||||
}
|
||||
return
|
||||
}
|
8
go.mod
Normal file
8
go.mod
Normal file
@ -0,0 +1,8 @@
|
||||
module github.com/Mrs4s/MiraiGo
|
||||
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/golang/protobuf v1.4.1
|
||||
google.golang.org/protobuf v1.25.0
|
||||
)
|
85
go.sum
Normal file
85
go.sum
Normal file
@ -0,0 +1,85 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/ishbir/elliptic v0.0.0-20150227224012-7fa75501baae h1:p4rT8RR3B5Q6rXTG5v0Tns45zn4wJucqQKF5hzrjtgU=
|
||||
github.com/ishbir/elliptic v0.0.0-20150227224012-7fa75501baae/go.mod h1:JPqapJUq3G3ojsWKJHZNsUK2OJmarnOxFN9afp3/Ovo=
|
||||
github.com/jhump/protoreflect v1.6.1 h1:4/2yi5LyDPP7nN+Hiird1SAJ6YoxUm13/oxHGRnbPd8=
|
||||
github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
227
message/elements.go
Normal file
227
message/elements.go
Normal file
@ -0,0 +1,227 @@
|
||||
package message
|
||||
|
||||
import "strings"
|
||||
|
||||
type TextElement struct {
|
||||
Content string
|
||||
}
|
||||
|
||||
type ImageElement struct {
|
||||
Filename string
|
||||
Url string
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type GroupImageElement struct {
|
||||
ImageId string
|
||||
Md5 []byte
|
||||
Url string
|
||||
}
|
||||
|
||||
type FaceElement struct {
|
||||
Index int32
|
||||
Name string
|
||||
}
|
||||
|
||||
func NewText(s string) *TextElement {
|
||||
return &TextElement{Content: s}
|
||||
}
|
||||
|
||||
func NewNetImage(filename, url string) *ImageElement {
|
||||
return &ImageElement{
|
||||
Filename: filename,
|
||||
Url: url,
|
||||
}
|
||||
}
|
||||
|
||||
func NewGroupImage(id string, md5 []byte) *GroupImageElement {
|
||||
return &GroupImageElement{
|
||||
ImageId: id,
|
||||
Md5: md5,
|
||||
Url: "http://gchat.qpic.cn/gchatpic_new/1/0-0-" + strings.ReplaceAll(id[1:36], "-", "") + "/0?term=2",
|
||||
}
|
||||
}
|
||||
|
||||
func NewFace(index int32) *FaceElement {
|
||||
name := faceMap[int(index)]
|
||||
if name == "" {
|
||||
name = "未知表情"
|
||||
}
|
||||
return &FaceElement{
|
||||
Index: index,
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *TextElement) Type() ElementType {
|
||||
return Text
|
||||
}
|
||||
|
||||
func (e *ImageElement) Type() ElementType {
|
||||
return Image
|
||||
}
|
||||
|
||||
func (e *FaceElement) Type() ElementType {
|
||||
return Face
|
||||
}
|
||||
|
||||
func (e *GroupImageElement) Type() ElementType {
|
||||
return Image
|
||||
}
|
||||
|
||||
var faceMap = map[int]string{
|
||||
14: "微笑",
|
||||
1: "撇嘴",
|
||||
2: "色",
|
||||
3: "发呆",
|
||||
4: "得意",
|
||||
5: "流泪",
|
||||
6: "害羞",
|
||||
7: "闭嘴",
|
||||
8: "睡",
|
||||
9: "大哭",
|
||||
10: "尴尬",
|
||||
11: "发怒",
|
||||
12: "调皮",
|
||||
13: "呲牙",
|
||||
0: "惊讶",
|
||||
15: "难过",
|
||||
16: "酷",
|
||||
96: "冷汗",
|
||||
18: "抓狂",
|
||||
19: "吐",
|
||||
20: "偷笑",
|
||||
21: "可爱",
|
||||
22: "白眼",
|
||||
23: "傲慢",
|
||||
24: "饥饿",
|
||||
25: "困",
|
||||
26: "惊恐",
|
||||
27: "流汗",
|
||||
28: "憨笑",
|
||||
29: "大兵",
|
||||
30: "奋斗",
|
||||
31: "咒骂",
|
||||
32: "疑问",
|
||||
33: "嘘",
|
||||
34: "晕",
|
||||
35: "折磨",
|
||||
36: "衰",
|
||||
37: "骷髅",
|
||||
38: "敲打",
|
||||
39: "再见",
|
||||
97: "擦汗",
|
||||
98: "抠鼻",
|
||||
99: "鼓掌",
|
||||
100: "糗大了",
|
||||
101: "坏笑",
|
||||
102: "左哼哼",
|
||||
103: "右哼哼",
|
||||
104: "哈欠",
|
||||
105: "鄙视",
|
||||
106: "委屈",
|
||||
107: "快哭了",
|
||||
108: "阴险",
|
||||
109: "亲亲",
|
||||
110: "吓",
|
||||
111: "可怜",
|
||||
172: "眨眼睛",
|
||||
182: "笑哭",
|
||||
179: "doge",
|
||||
173: "泪奔",
|
||||
174: "无奈",
|
||||
212: "托腮",
|
||||
175: "卖萌",
|
||||
178: "斜眼笑",
|
||||
177: "喷血",
|
||||
180: "惊喜",
|
||||
181: "骚扰",
|
||||
176: "小纠结",
|
||||
183: "我最美",
|
||||
112: "菜刀",
|
||||
89: "西瓜",
|
||||
113: "啤酒",
|
||||
114: "篮球",
|
||||
115: "乒乓",
|
||||
171: "茶",
|
||||
60: "咖啡",
|
||||
61: "饭",
|
||||
46: "猪头",
|
||||
63: "玫瑰",
|
||||
64: "凋谢",
|
||||
116: "示爱",
|
||||
66: "爱心",
|
||||
67: "心碎",
|
||||
53: "蛋糕",
|
||||
54: "闪电",
|
||||
55: "炸弹",
|
||||
56: "刀",
|
||||
57: "足球",
|
||||
117: "瓢虫",
|
||||
59: "便便",
|
||||
75: "月亮",
|
||||
74: "太阳",
|
||||
69: "礼物",
|
||||
49: "拥抱",
|
||||
76: "强",
|
||||
77: "弱",
|
||||
78: "握手",
|
||||
79: "胜利",
|
||||
118: "抱拳",
|
||||
119: "勾引",
|
||||
120: "拳头",
|
||||
121: "差劲",
|
||||
122: "爱你",
|
||||
123: "NO",
|
||||
124: "OK",
|
||||
42: "爱情",
|
||||
85: "飞吻",
|
||||
43: "跳跳",
|
||||
41: "发抖",
|
||||
86: "怄火",
|
||||
125: "转圈",
|
||||
126: "磕头",
|
||||
127: "回头",
|
||||
128: "跳绳",
|
||||
129: "挥手",
|
||||
130: "激动",
|
||||
131: "街舞",
|
||||
132: "献吻",
|
||||
133: "左太极",
|
||||
134: "右太极",
|
||||
136: "双喜",
|
||||
137: "鞭炮",
|
||||
138: "灯笼",
|
||||
140: "K歌",
|
||||
144: "喝彩",
|
||||
145: "祈祷",
|
||||
146: "爆筋",
|
||||
147: "棒棒糖",
|
||||
148: "喝奶",
|
||||
151: "飞机",
|
||||
158: "钞票",
|
||||
168: "药",
|
||||
169: "手枪",
|
||||
188: "蛋",
|
||||
192: "红包",
|
||||
184: "河蟹",
|
||||
185: "羊驼",
|
||||
190: "菊花",
|
||||
187: "幽灵",
|
||||
193: "大笑",
|
||||
194: "不开心",
|
||||
197: "冷漠",
|
||||
198: "呃",
|
||||
199: "好棒",
|
||||
200: "拜托",
|
||||
201: "点赞",
|
||||
202: "无聊",
|
||||
203: "托脸",
|
||||
204: "吃",
|
||||
205: "送花",
|
||||
206: "害怕",
|
||||
207: "花痴",
|
||||
208: "小样儿",
|
||||
210: "飙泪",
|
||||
211: "我不看",
|
||||
}
|
116
message/message.go
Normal file
116
message/message.go
Normal file
@ -0,0 +1,116 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
||||
)
|
||||
|
||||
type PrivateMessage struct {
|
||||
Id int32
|
||||
Sender *Sender
|
||||
Elements []IMessageElement
|
||||
}
|
||||
|
||||
type GroupMessage struct {
|
||||
Id int32
|
||||
GroupUin int64
|
||||
GroupName string
|
||||
Sender *Sender
|
||||
Elements []IMessageElement
|
||||
IsAdministrator bool
|
||||
}
|
||||
|
||||
type SendingMessage struct {
|
||||
Elements []IMessageElement
|
||||
}
|
||||
|
||||
type Sender struct {
|
||||
Uin int64
|
||||
Nickname string
|
||||
CardName string
|
||||
IsFriend bool
|
||||
}
|
||||
|
||||
type IMessageElement interface {
|
||||
Type() ElementType
|
||||
}
|
||||
|
||||
type ElementType int
|
||||
|
||||
const (
|
||||
Text ElementType = iota
|
||||
Image
|
||||
Face
|
||||
)
|
||||
|
||||
func (s *Sender) IsAnonymous() bool {
|
||||
return s.Uin == 80000000
|
||||
}
|
||||
|
||||
func (msg *PrivateMessage) ToString() (res string) {
|
||||
for _, elem := range msg.Elements {
|
||||
switch e := elem.(type) {
|
||||
case *TextElement:
|
||||
res += e.Content
|
||||
case *ImageElement:
|
||||
res += " [Image= " + e.Filename + " ] "
|
||||
case *FaceElement:
|
||||
res += " [" + e.Name + "] "
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (msg *GroupMessage) ToString() (res string) {
|
||||
for _, elem := range msg.Elements {
|
||||
switch e := elem.(type) {
|
||||
case *TextElement:
|
||||
res += e.Content
|
||||
case *ImageElement:
|
||||
res += " [Image= " + e.Filename + " ] "
|
||||
case *FaceElement:
|
||||
res += " [" + e.Name + "] "
|
||||
case *GroupImageElement:
|
||||
res += "[Image= " + e.ImageId + " ]"
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (msg *SendingMessage) Append(e IMessageElement) *SendingMessage {
|
||||
msg.Elements = append(msg.Elements, e)
|
||||
return msg
|
||||
}
|
||||
|
||||
func ToProtoElems(elems []IMessageElement) (r []*msg.Elem) {
|
||||
for _, elem := range elems {
|
||||
switch e := elem.(type) {
|
||||
case *TextElement:
|
||||
r = append(r, &msg.Elem{
|
||||
Text: &msg.Text{
|
||||
Str: e.Content,
|
||||
},
|
||||
})
|
||||
case *FaceElement:
|
||||
r = append(r, &msg.Elem{
|
||||
Face: &msg.Face{
|
||||
Index: e.Index,
|
||||
Old: binary.ToBytes(int16(0x1445 - 4 + e.Index)),
|
||||
Buf: []byte{0x00, 0x01, 0x00, 0x04, 0x52, 0xCC, 0xF5, 0xD0},
|
||||
},
|
||||
})
|
||||
case *GroupImageElement:
|
||||
r = append(r, &msg.Elem{
|
||||
CustomFace: &msg.CustomFace{
|
||||
FilePath: e.ImageId,
|
||||
Md5: e.Md5[:],
|
||||
Flag: make([]byte, 4),
|
||||
OldData: []byte{0x15, 0x36, 0x20, 0x39, 0x32, 0x6B, 0x41, 0x31, 0x00, 0x38, 0x37, 0x32, 0x66, 0x30, 0x36, 0x36, 0x30, 0x33, 0x61, 0x65, 0x31, 0x30, 0x33, 0x62, 0x37, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x35, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7B, 0x30, 0x31, 0x45, 0x39, 0x34, 0x35, 0x31, 0x42, 0x2D, 0x37, 0x30, 0x45, 0x44,
|
||||
0x2D, 0x45, 0x41, 0x45, 0x33, 0x2D, 0x42, 0x33, 0x37, 0x43, 0x2D, 0x31, 0x30, 0x31, 0x46, 0x31, 0x45, 0x45, 0x42, 0x46, 0x35, 0x42, 0x35, 0x7D, 0x2E, 0x70, 0x6E, 0x67, 0x41},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
35
protocol/crypto/crypto.go
Normal file
35
protocol/crypto/crypto.go
Normal file
@ -0,0 +1,35 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
)
|
||||
|
||||
type EncryptECDH struct {
|
||||
InitialShareKey []byte
|
||||
PublicKey []byte
|
||||
}
|
||||
|
||||
var ECDH = &EncryptECDH{}
|
||||
|
||||
func init() {
|
||||
//TODO: Keygen
|
||||
ECDH.InitialShareKey, _ = hex.DecodeString("41d0d17c506a5256d0d08d7aac133c70")
|
||||
ECDH.PublicKey, _ = hex.DecodeString("049fb03421ba7ab5fc91c2d94a7657fff7ba8fe09f08a22951a24865212cbc45aff1b5125188fa8f0e30473bc55d54edc2")
|
||||
}
|
||||
|
||||
func (e *EncryptECDH) DoEncrypt(d, k []byte) []byte {
|
||||
w := binary.NewWriter()
|
||||
w.WriteByte(0x01)
|
||||
w.WriteByte(0x01)
|
||||
w.Write(k)
|
||||
w.WriteUInt16(258)
|
||||
w.WriteUInt16(uint16(len(ECDH.PublicKey)))
|
||||
w.Write(ECDH.PublicKey)
|
||||
w.EncryptAndWrite(ECDH.InitialShareKey, d)
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
func (e *EncryptECDH) Id() byte {
|
||||
return 7
|
||||
}
|
1
protocol/crypto/ecdh.go
Normal file
1
protocol/crypto/ecdh.go
Normal file
@ -0,0 +1 @@
|
||||
package crypto
|
233
protocol/crypto/secp192k1.go
Normal file
233
protocol/crypto/secp192k1.go
Normal file
@ -0,0 +1,233 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"io"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// A BitCurve represents a Koblitz Curve with a=0.
|
||||
// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw.html
|
||||
type BitCurve struct {
|
||||
P *big.Int // the order of the underlying field
|
||||
N *big.Int // the order of the base point
|
||||
B *big.Int // the constant of the BitCurve equation
|
||||
Gx, Gy *big.Int // (x,y) of the base point
|
||||
BitSize int // the size of the underlying field
|
||||
}
|
||||
|
||||
// See FIPS 186-3, section D.2.2.1
|
||||
// And http://www.secg.org/sec2-v2.pdf section 2.2.1
|
||||
var secp192k1 = &BitCurve{
|
||||
P: new(big.Int).SetBytes([]byte{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xEE, 0x37,
|
||||
}), N: new(big.Int).SetBytes([]byte{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
|
||||
0x26, 0xF2, 0xFC, 0x17, 0x0F, 0x69, 0x46, 0x6A, 0x74, 0xDE, 0xFD, 0x8D,
|
||||
}), B: new(big.Int).SetBytes([]byte{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
|
||||
}), Gx: new(big.Int).SetBytes([]byte{
|
||||
0xDB, 0x4F, 0xF1, 0x0E, 0xC0, 0x57, 0xE9, 0xAE, 0x26, 0xB0, 0x7D, 0x02,
|
||||
0x80, 0xB7, 0xF4, 0x34, 0x1D, 0xA5, 0xD1, 0xB1, 0xEA, 0xE0, 0x6C, 0x7D,
|
||||
}), Gy: new(big.Int).SetBytes([]byte{
|
||||
0x9B, 0x2F, 0x2F, 0x6D, 0x9C, 0x56, 0x28, 0xA7, 0x84, 0x41, 0x63, 0xD0,
|
||||
0x15, 0xBE, 0x86, 0x34, 0x40, 0x82, 0xAA, 0x88, 0xD9, 0x5E, 0x2F, 0x9D,
|
||||
}),
|
||||
BitSize: 192,
|
||||
}
|
||||
|
||||
func (BitCurve *BitCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) {
|
||||
zinv := new(big.Int).ModInverse(z, BitCurve.P)
|
||||
zinvsq := new(big.Int).Mul(zinv, zinv)
|
||||
|
||||
xOut = new(big.Int).Mul(x, zinvsq)
|
||||
xOut.Mod(xOut, BitCurve.P)
|
||||
zinvsq.Mul(zinvsq, zinv)
|
||||
yOut = new(big.Int).Mul(y, zinvsq)
|
||||
yOut.Mod(yOut, BitCurve.P)
|
||||
return
|
||||
}
|
||||
|
||||
// addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and
|
||||
// (x2, y2, z2) and returns their sum, also in Jacobian form.
|
||||
func (BitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) {
|
||||
// See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
|
||||
z1z1 := new(big.Int).Mul(z1, z1)
|
||||
z1z1.Mod(z1z1, BitCurve.P)
|
||||
z2z2 := new(big.Int).Mul(z2, z2)
|
||||
z2z2.Mod(z2z2, BitCurve.P)
|
||||
|
||||
u1 := new(big.Int).Mul(x1, z2z2)
|
||||
u1.Mod(u1, BitCurve.P)
|
||||
u2 := new(big.Int).Mul(x2, z1z1)
|
||||
u2.Mod(u2, BitCurve.P)
|
||||
h := new(big.Int).Sub(u2, u1)
|
||||
if h.Sign() == -1 {
|
||||
h.Add(h, BitCurve.P)
|
||||
}
|
||||
i := new(big.Int).Lsh(h, 1)
|
||||
i.Mul(i, i)
|
||||
j := new(big.Int).Mul(h, i)
|
||||
|
||||
s1 := new(big.Int).Mul(y1, z2)
|
||||
s1.Mul(s1, z2z2)
|
||||
s1.Mod(s1, BitCurve.P)
|
||||
s2 := new(big.Int).Mul(y2, z1)
|
||||
s2.Mul(s2, z1z1)
|
||||
s2.Mod(s2, BitCurve.P)
|
||||
r := new(big.Int).Sub(s2, s1)
|
||||
if r.Sign() == -1 {
|
||||
r.Add(r, BitCurve.P)
|
||||
}
|
||||
r.Lsh(r, 1)
|
||||
v := new(big.Int).Mul(u1, i)
|
||||
|
||||
x3 := new(big.Int).Set(r)
|
||||
x3.Mul(x3, x3)
|
||||
x3.Sub(x3, j)
|
||||
x3.Sub(x3, v)
|
||||
x3.Sub(x3, v)
|
||||
x3.Mod(x3, BitCurve.P)
|
||||
|
||||
y3 := new(big.Int).Set(r)
|
||||
v.Sub(v, x3)
|
||||
y3.Mul(y3, v)
|
||||
s1.Mul(s1, j)
|
||||
s1.Lsh(s1, 1)
|
||||
y3.Sub(y3, s1)
|
||||
y3.Mod(y3, BitCurve.P)
|
||||
|
||||
z3 := new(big.Int).Add(z1, z2)
|
||||
z3.Mul(z3, z3)
|
||||
z3.Sub(z3, z1z1)
|
||||
if z3.Sign() == -1 {
|
||||
z3.Add(z3, BitCurve.P)
|
||||
}
|
||||
z3.Sub(z3, z2z2)
|
||||
if z3.Sign() == -1 {
|
||||
z3.Add(z3, BitCurve.P)
|
||||
}
|
||||
z3.Mul(z3, h)
|
||||
z3.Mod(z3, BitCurve.P)
|
||||
|
||||
return x3, y3, z3
|
||||
}
|
||||
|
||||
// doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and
|
||||
// returns its double, also in Jacobian form.
|
||||
func (BitCurve *BitCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) {
|
||||
// See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
|
||||
|
||||
a := new(big.Int).Mul(x, x) //X1²
|
||||
b := new(big.Int).Mul(y, y) //Y1²
|
||||
c := new(big.Int).Mul(b, b) //B²
|
||||
|
||||
d := new(big.Int).Add(x, b) //X1+B
|
||||
d.Mul(d, d) //(X1+B)²
|
||||
d.Sub(d, a) //(X1+B)²-A
|
||||
d.Sub(d, c) //(X1+B)²-A-C
|
||||
d.Mul(d, big.NewInt(2)) //2*((X1+B)²-A-C)
|
||||
|
||||
e := new(big.Int).Mul(big.NewInt(3), a) //3*A
|
||||
f := new(big.Int).Mul(e, e) //E²
|
||||
|
||||
x3 := new(big.Int).Mul(big.NewInt(2), d) //2*D
|
||||
x3.Sub(f, x3) //F-2*D
|
||||
x3.Mod(x3, BitCurve.P)
|
||||
|
||||
y3 := new(big.Int).Sub(d, x3) //D-X3
|
||||
y3.Mul(e, y3) //E*(D-X3)
|
||||
y3.Sub(y3, new(big.Int).Mul(big.NewInt(8), c)) //E*(D-X3)-8*C
|
||||
y3.Mod(y3, BitCurve.P)
|
||||
|
||||
z3 := new(big.Int).Mul(y, z) //Y1*Z1
|
||||
z3.Mul(big.NewInt(2), z3) //3*Y1*Z1
|
||||
z3.Mod(z3, BitCurve.P)
|
||||
|
||||
return x3, y3, z3
|
||||
}
|
||||
|
||||
//TODO: double check if it is okay
|
||||
// ScalarMult returns k*(Bx,By) where k is a number in big-endian form.
|
||||
func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) {
|
||||
// We have a slight problem in that the identity of the group (the
|
||||
// point at infinity) cannot be represented in (x, y) form on a finite
|
||||
// machine. Thus the standard add/double algorithm has to be tweaked
|
||||
// slightly: our initial state is not the identity, but x, and we
|
||||
// ignore the first true bit in |k|. If we don't find any true bits in
|
||||
// |k|, then we return nil, nil, because we cannot return the identity
|
||||
// element.
|
||||
|
||||
Bz := new(big.Int).SetInt64(1)
|
||||
x := Bx
|
||||
y := By
|
||||
z := Bz
|
||||
|
||||
seenFirstTrue := false
|
||||
for _, byte := range k {
|
||||
for bitNum := 0; bitNum < 8; bitNum++ {
|
||||
if seenFirstTrue {
|
||||
x, y, z = BitCurve.doubleJacobian(x, y, z)
|
||||
}
|
||||
if byte&0x80 == 0x80 {
|
||||
if !seenFirstTrue {
|
||||
seenFirstTrue = true
|
||||
} else {
|
||||
x, y, z = BitCurve.addJacobian(Bx, By, Bz, x, y, z)
|
||||
}
|
||||
}
|
||||
byte <<= 1
|
||||
}
|
||||
}
|
||||
|
||||
if !seenFirstTrue {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return BitCurve.affineFromJacobian(x, y, z)
|
||||
}
|
||||
|
||||
// ScalarBaseMult returns k*G, where G is the base point of the group and k is
|
||||
// an integer in big-endian form.
|
||||
func (BitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
|
||||
return BitCurve.ScalarMult(BitCurve.Gx, BitCurve.Gy, k)
|
||||
}
|
||||
|
||||
var mask = []byte{0xff, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f}
|
||||
|
||||
//TODO: double check if it is okay
|
||||
// GenerateKey returns a public/private key pair. The private key is generated
|
||||
// using the given reader, which must return random data.
|
||||
func (BitCurve *BitCurve) GenerateKey(rand io.Reader) (priv []byte, x, y *big.Int, err error) {
|
||||
byteLen := (BitCurve.BitSize + 7) >> 3
|
||||
priv = make([]byte, byteLen)
|
||||
|
||||
for x == nil {
|
||||
_, err = io.ReadFull(rand, priv)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// We have to mask off any excess bits in the case that the size of the
|
||||
// underlying field is not a whole number of bytes.
|
||||
priv[0] &= mask[BitCurve.BitSize%8]
|
||||
// This is because, in tests, rand will return all zeros and we don't
|
||||
// want to get the point at infinity and loop forever.
|
||||
priv[1] ^= 0x42
|
||||
x, y = BitCurve.ScalarBaseMult(priv)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
$ openssl asn1parse -in 1.cer -inform DER -dump
|
||||
0:d=0 hl=2 l= 70 cons: SEQUENCE
|
||||
2:d=1 hl=2 l= 16 cons: SEQUENCE
|
||||
4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey
|
||||
13:d=2 hl=2 l= 5 prim: OBJECT :secp192k1
|
||||
20:d=1 hl=2 l= 50 prim: BIT STRING
|
||||
0000 - 00 04 92 8d 88 50 67 30-88 b3 43 26 4e 0c 6b ac .....Pg0..C&N.k.
|
||||
0010 - b8 49 6d 69 77 99 f3 72-11 de b2 5b b7 39 06 cb .Imiw..r...[.9..
|
||||
0020 - 08 9f ea 96 39 b4 e0 26-04 98 b5 1a 99 2d 50 81 ....9..&.....-P.
|
||||
0030 - 3d a8 =.
|
||||
*/
|
40
protocol/packets/builders.go
Normal file
40
protocol/packets/builders.go
Normal file
@ -0,0 +1,40 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func BuildLoginPacket(uin int64, bodyType byte, key, body, extraData []byte) []byte {
|
||||
w := binary.NewWriter()
|
||||
w.WriteIntLvPacket(4, func(w *binary.Writer) {
|
||||
w.WriteUInt32(0x00_00_00_0A)
|
||||
w.WriteByte(bodyType)
|
||||
w.WriteIntLvPacket(4, func(w *binary.Writer) {
|
||||
w.Write(extraData)
|
||||
})
|
||||
w.WriteByte(0x00)
|
||||
w.WriteString(strconv.FormatInt(uin, 10))
|
||||
if len(key) == 0 {
|
||||
w.Write(body)
|
||||
} else {
|
||||
w.EncryptAndWrite(key, body)
|
||||
}
|
||||
})
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
func BuildUniPacket(uin int64, seq uint16, commandName string, bodyType byte, sessionId, extraData, key, body []byte) []byte {
|
||||
w := binary.NewWriter()
|
||||
w.WriteIntLvPacket(4, func(w *binary.Writer) {
|
||||
w.WriteUInt32(0x0B)
|
||||
w.WriteByte(bodyType)
|
||||
w.WriteUInt32(uint32(seq))
|
||||
w.WriteByte(0)
|
||||
w.WriteString(strconv.FormatInt(uin, 10))
|
||||
w.EncryptAndWrite(key, binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUniPacket(commandName, sessionId, extraData, body)
|
||||
}))
|
||||
})
|
||||
return w.Bytes()
|
||||
}
|
195
protocol/packets/global.go
Normal file
195
protocol/packets/global.go
Normal file
@ -0,0 +1,195 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
"github.com/Mrs4s/MiraiGo/protocol/crypto"
|
||||
)
|
||||
|
||||
var ErrUnknownFlag = errors.New("unknown flag")
|
||||
var ErrDecryptFailed = errors.New("decrypt failed")
|
||||
|
||||
type ISendingPacket interface {
|
||||
CommandId() uint16
|
||||
Writer() *binary.Writer
|
||||
}
|
||||
|
||||
type IncomingPacket struct {
|
||||
SequenceId uint16
|
||||
Flag2 byte
|
||||
CommandName string
|
||||
SessionId []byte
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
type IEncryptMethod interface {
|
||||
DoEncrypt([]byte, []byte) []byte
|
||||
Id() byte
|
||||
}
|
||||
|
||||
func BuildOicqRequestPacket(uin int64, commandId uint16, encrypt IEncryptMethod, key []byte, bodyFunc func(writer *binary.Writer)) []byte {
|
||||
b := binary.NewWriter()
|
||||
bodyFunc(b)
|
||||
|
||||
body := encrypt.DoEncrypt(b.Bytes(), key)
|
||||
p := binary.NewWriter()
|
||||
p.WriteByte(0x02)
|
||||
p.WriteUInt16(27 + 2 + uint16(len(body)))
|
||||
p.WriteUInt16(8001)
|
||||
p.WriteUInt16(commandId)
|
||||
p.WriteUInt16(1)
|
||||
p.WriteUInt32(uint32(uin))
|
||||
p.WriteByte(3)
|
||||
p.WriteByte(encrypt.Id())
|
||||
p.WriteByte(0)
|
||||
p.WriteUInt32(2)
|
||||
p.WriteUInt32(0)
|
||||
p.WriteUInt32(0)
|
||||
p.Write(body)
|
||||
p.WriteByte(0x03)
|
||||
return p.Bytes()
|
||||
}
|
||||
|
||||
func BuildSsoPacket(seq uint16, commandName, imei string, extData, outPacketSessionId, body, ksid []byte) []byte {
|
||||
p := binary.NewWriter()
|
||||
p.WriteIntLvPacket(4, func(writer *binary.Writer) {
|
||||
writer.WriteUInt32(uint32(seq))
|
||||
writer.WriteUInt32(537062409) // Android pad (sub app id)
|
||||
writer.WriteUInt32(537062409)
|
||||
writer.Write([]byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00})
|
||||
if len(extData) == 0 || len(extData) == 4 {
|
||||
writer.WriteUInt32(0x04)
|
||||
} else {
|
||||
writer.WriteUInt32(uint32(len(extData) + 4))
|
||||
writer.Write(extData)
|
||||
}
|
||||
writer.WriteString(commandName)
|
||||
writer.WriteUInt32(0x08)
|
||||
writer.Write(outPacketSessionId)
|
||||
writer.WriteString(imei)
|
||||
writer.WriteUInt32(0x04)
|
||||
{
|
||||
writer.WriteUInt16(uint16(len(ksid)) + 2)
|
||||
writer.Write(ksid)
|
||||
}
|
||||
writer.WriteUInt32(0x04)
|
||||
})
|
||||
|
||||
p.WriteIntLvPacket(4, func(writer *binary.Writer) {
|
||||
writer.Write(body)
|
||||
})
|
||||
return p.Bytes()
|
||||
}
|
||||
|
||||
func ParseIncomingPacket(payload, d2key []byte) (*IncomingPacket, error) {
|
||||
reader := binary.NewReader(payload)
|
||||
flag1 := reader.ReadInt32()
|
||||
flag2 := reader.ReadByte()
|
||||
if reader.ReadByte() != 0 { // flag3
|
||||
return nil, ErrUnknownFlag
|
||||
}
|
||||
reader.ReadString() // uin string
|
||||
decrypted := func() (data []byte) {
|
||||
defer func() {
|
||||
if pan := recover(); pan != nil {
|
||||
// TODO: bot.client.tryDecryptOrNull
|
||||
}
|
||||
}()
|
||||
switch flag2 {
|
||||
case 0:
|
||||
return reader.ReadAvailable()
|
||||
case 1:
|
||||
d2 := binary.NewTeaCipher(d2key)
|
||||
return d2.Decrypt(reader.ReadAvailable())
|
||||
case 2:
|
||||
z16 := binary.NewTeaCipher(make([]byte, 16))
|
||||
return z16.Decrypt(reader.ReadAvailable())
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
if len(decrypted) == 0 {
|
||||
return nil, ErrDecryptFailed
|
||||
}
|
||||
if flag1 != 0x0A && flag1 != 0x0B {
|
||||
return nil, ErrDecryptFailed
|
||||
}
|
||||
return parseSsoFrame(decrypted, flag2), nil
|
||||
}
|
||||
|
||||
func parseSsoFrame(payload []byte, flag2 byte) *IncomingPacket {
|
||||
reader := binary.NewReader(payload)
|
||||
reader.ReadInt32() // packet len
|
||||
seqId := reader.ReadInt32()
|
||||
reader.ReadInt32() // return code
|
||||
reader.ReadBytes(int(reader.ReadInt32()) - 4) // extra data
|
||||
commandName := reader.ReadString()
|
||||
sessionId := reader.ReadBytes(int(reader.ReadInt32()) - 4)
|
||||
if commandName == "Heartbeat.Alive" {
|
||||
return &IncomingPacket{
|
||||
SequenceId: uint16(seqId),
|
||||
Flag2: flag2,
|
||||
CommandName: commandName,
|
||||
SessionId: sessionId,
|
||||
Payload: []byte{},
|
||||
}
|
||||
}
|
||||
compressedFlag := reader.ReadInt32()
|
||||
packet := func() []byte {
|
||||
if compressedFlag == 0 {
|
||||
pktSize := uint64(reader.ReadInt32()) & 0xffffffff
|
||||
if pktSize == uint64(reader.Len()) || pktSize == uint64(reader.Len()+4) {
|
||||
return reader.ReadAvailable()
|
||||
} else {
|
||||
return reader.ReadAvailable() // some logic
|
||||
}
|
||||
}
|
||||
if compressedFlag == 1 {
|
||||
reader.ReadBytes(4)
|
||||
return binary.ZlibUncompress(reader.ReadAvailable()) // ?
|
||||
}
|
||||
if compressedFlag == 8 {
|
||||
return reader.ReadAvailable()
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
return &IncomingPacket{
|
||||
SequenceId: uint16(seqId),
|
||||
Flag2: flag2,
|
||||
CommandName: commandName,
|
||||
SessionId: sessionId,
|
||||
Payload: packet,
|
||||
}
|
||||
}
|
||||
|
||||
func (pkt *IncomingPacket) DecryptPayload(random []byte) ([]byte, error) {
|
||||
reader := binary.NewReader(pkt.Payload)
|
||||
if reader.ReadByte() != 2 {
|
||||
return nil, ErrUnknownFlag
|
||||
}
|
||||
reader.ReadBytes(2)
|
||||
reader.ReadBytes(2)
|
||||
reader.ReadUInt16()
|
||||
reader.ReadUInt16()
|
||||
reader.ReadInt32()
|
||||
encryptType := reader.ReadUInt16()
|
||||
reader.ReadByte()
|
||||
if encryptType == 0 {
|
||||
data := func() (decrypted []byte) {
|
||||
d := reader.ReadBytes(reader.Len() - 1)
|
||||
defer func() {
|
||||
if pan := recover(); pan != nil {
|
||||
tea := binary.NewTeaCipher(random)
|
||||
decrypted = tea.Decrypt(d)
|
||||
}
|
||||
}()
|
||||
tea := binary.NewTeaCipher(crypto.ECDH.InitialShareKey)
|
||||
decrypted = tea.Decrypt(d)
|
||||
return
|
||||
}()
|
||||
return data, nil
|
||||
}
|
||||
if encryptType == 4 {
|
||||
panic("todo")
|
||||
}
|
||||
return nil, ErrUnknownFlag
|
||||
}
|
1
protocol/protocol_global.go
Normal file
1
protocol/protocol_global.go
Normal file
@ -0,0 +1 @@
|
||||
package protocol
|
24
protocol/tlv/t1.go
Normal file
24
protocol/tlv/t1.go
Normal file
@ -0,0 +1,24 @@
|
||||
package tlv
|
||||
|
||||
import (
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
func T1(uin uint32, ip []byte) []byte {
|
||||
if len(ip) != 4 {
|
||||
panic("invalid ip")
|
||||
}
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x01)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(1)
|
||||
w.WriteUInt32(rand.Uint32())
|
||||
w.WriteUInt32(uin)
|
||||
w.WriteUInt32(uint32(time.Now().UnixNano() / 1e6))
|
||||
w.Write(ip)
|
||||
w.WriteUInt16(0)
|
||||
}))
|
||||
})
|
||||
}
|
17
protocol/tlv/t100.go
Normal file
17
protocol/tlv/t100.go
Normal file
@ -0,0 +1,17 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T100() []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x100)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(1)
|
||||
w.WriteUInt32(5)
|
||||
w.WriteUInt32(16)
|
||||
w.WriteUInt32(537062409) // Sub app id
|
||||
w.WriteUInt32(0) // App client version
|
||||
w.WriteUInt32(34869472)
|
||||
}))
|
||||
})
|
||||
}
|
10
protocol/tlv/t104.go
Normal file
10
protocol/tlv/t104.go
Normal file
@ -0,0 +1,10 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T104(data []byte) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x104)
|
||||
w.WriteTlv(data)
|
||||
})
|
||||
}
|
57
protocol/tlv/t106.go
Normal file
57
protocol/tlv/t106.go
Normal file
@ -0,0 +1,57 @@
|
||||
package tlv
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
binary2 "encoding/binary"
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
func T106(uin, salt uint32, passwordMd5 [16]byte, guidAvailable bool, guid, tgtgtKey []byte) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x106)
|
||||
body := binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(4)
|
||||
w.WriteUInt32(rand.Uint32())
|
||||
w.WriteUInt32(5)
|
||||
w.WriteUInt32(16) // appId
|
||||
w.WriteUInt32(0) // app client version
|
||||
if uin == 0 {
|
||||
w.WriteUInt64(uint64(salt))
|
||||
} else {
|
||||
w.WriteUInt64(uint64(uin))
|
||||
}
|
||||
w.WriteUInt32(uint32(time.Now().UnixNano() / 1e6))
|
||||
w.Write([]byte{0x00, 0x00, 0x00, 0x00}) // fake ip
|
||||
w.WriteByte(0x01)
|
||||
w.Write(passwordMd5[:])
|
||||
w.Write(tgtgtKey)
|
||||
w.WriteUInt32(0)
|
||||
w.WriteBool(guidAvailable)
|
||||
if len(guid) == 0 {
|
||||
for i := 0; i < 4; i++ {
|
||||
w.WriteUInt32(rand.Uint32())
|
||||
}
|
||||
} else {
|
||||
w.Write(guid)
|
||||
}
|
||||
w.WriteUInt32(537062409) // sub app id (android pad)
|
||||
w.WriteUInt32(1) // password login
|
||||
b := make([]byte, 8)
|
||||
binary2.BigEndian.PutUint64(b, uint64(uin))
|
||||
w.WriteTlv(b)
|
||||
w.WriteUInt16(0)
|
||||
})
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
b := make([]byte, 4)
|
||||
if salt != 0 {
|
||||
binary2.BigEndian.PutUint32(b, salt)
|
||||
} else {
|
||||
binary2.BigEndian.PutUint32(b, uin)
|
||||
}
|
||||
key := md5.Sum(append(append(passwordMd5[:], []byte{0x00, 0x00, 0x00, 0x00}...), b...))
|
||||
w.EncryptAndWrite(key[:], body)
|
||||
}))
|
||||
})
|
||||
}
|
15
protocol/tlv/t107.go
Normal file
15
protocol/tlv/t107.go
Normal file
@ -0,0 +1,15 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T107(picType uint16) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x107)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(picType)
|
||||
w.WriteByte(0x00)
|
||||
w.WriteUInt16(0)
|
||||
w.WriteByte(0x01)
|
||||
}))
|
||||
})
|
||||
}
|
16
protocol/tlv/t109.go
Normal file
16
protocol/tlv/t109.go
Normal file
@ -0,0 +1,16 @@
|
||||
package tlv
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
)
|
||||
|
||||
func T109(androidId []byte) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x109)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
h := md5.Sum(androidId)
|
||||
w.Write(h[:])
|
||||
}))
|
||||
})
|
||||
}
|
16
protocol/tlv/t116.go
Normal file
16
protocol/tlv/t116.go
Normal file
@ -0,0 +1,16 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T116(miscBitmap, subSigMap uint32) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x116)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteByte(0x00)
|
||||
w.WriteUInt32(miscBitmap)
|
||||
w.WriteUInt32(subSigMap)
|
||||
w.WriteByte(0x01)
|
||||
w.WriteUInt32(1600000226) // app id list
|
||||
}))
|
||||
})
|
||||
}
|
17
protocol/tlv/t124.go
Normal file
17
protocol/tlv/t124.go
Normal file
@ -0,0 +1,17 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T124(osType, osVersion, simInfo, apn []byte) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x124)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteTlvLimitedSize(osType, 16)
|
||||
w.WriteTlvLimitedSize(osVersion, 16)
|
||||
w.WriteUInt16(2) // Network type wifi
|
||||
w.WriteTlvLimitedSize(simInfo, 16)
|
||||
w.WriteTlvLimitedSize([]byte{}, 16)
|
||||
w.WriteTlvLimitedSize(apn, 16)
|
||||
}))
|
||||
})
|
||||
}
|
19
protocol/tlv/t128.go
Normal file
19
protocol/tlv/t128.go
Normal file
@ -0,0 +1,19 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T128(isGuidFromFileNull, isGuidAvailable, isGuidChanged bool, guidFlag uint32, buildModel, guid, buildBrand []byte) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x128)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0)
|
||||
w.WriteBool(isGuidFromFileNull)
|
||||
w.WriteBool(isGuidAvailable)
|
||||
w.WriteBool(isGuidChanged)
|
||||
w.WriteUInt32(guidFlag)
|
||||
w.WriteTlvLimitedSize(buildModel, 32)
|
||||
w.WriteTlvLimitedSize(guid, 16)
|
||||
w.WriteTlvLimitedSize(buildBrand, 16)
|
||||
}))
|
||||
})
|
||||
}
|
15
protocol/tlv/t141.go
Normal file
15
protocol/tlv/t141.go
Normal file
@ -0,0 +1,15 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T141(simInfo, apn []byte) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x141)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(1)
|
||||
w.WriteTlv(simInfo)
|
||||
w.WriteUInt16(2) // network type wifi
|
||||
w.WriteTlv(apn)
|
||||
}))
|
||||
})
|
||||
}
|
13
protocol/tlv/t142.go
Normal file
13
protocol/tlv/t142.go
Normal file
@ -0,0 +1,13 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T142(apkId string) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x142)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0)
|
||||
w.WriteTlvLimitedSize([]byte(apkId), 32)
|
||||
}))
|
||||
})
|
||||
}
|
24
protocol/tlv/t144.go
Normal file
24
protocol/tlv/t144.go
Normal file
@ -0,0 +1,24 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T144(
|
||||
androidId, devInfo, osType, osVersion, simInfo, apn []byte,
|
||||
isGuidFromFileNull, isGuidAvailable, isGuidChanged bool,
|
||||
guidFlag uint32,
|
||||
buildModel, guid, buildBrand, tgtgtKey []byte,
|
||||
) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x144)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.EncryptAndWrite(tgtgtKey, binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(5)
|
||||
w.Write(T109(androidId))
|
||||
w.Write(T52D(devInfo))
|
||||
w.Write(T124(osType, osVersion, simInfo, apn))
|
||||
w.Write(T128(isGuidFromFileNull, isGuidAvailable, isGuidChanged, guidFlag, buildModel, guid, buildBrand))
|
||||
w.Write(T16E(buildModel))
|
||||
}))
|
||||
}))
|
||||
})
|
||||
}
|
12
protocol/tlv/t145.go
Normal file
12
protocol/tlv/t145.go
Normal file
@ -0,0 +1,12 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T145(guid []byte) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x145)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.Write(guid)
|
||||
}))
|
||||
})
|
||||
}
|
14
protocol/tlv/t147.go
Normal file
14
protocol/tlv/t147.go
Normal file
@ -0,0 +1,14 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T147(appId uint32, apkVersionName, apkSignatureMd5 []byte) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x147)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt32(appId)
|
||||
w.WriteTlvLimitedSize(apkVersionName, 32)
|
||||
w.WriteTlvLimitedSize(apkSignatureMd5, 32)
|
||||
}))
|
||||
})
|
||||
}
|
12
protocol/tlv/t154.go
Normal file
12
protocol/tlv/t154.go
Normal file
@ -0,0 +1,12 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T154(seq uint16) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x154)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt32(uint32(seq))
|
||||
}))
|
||||
})
|
||||
}
|
12
protocol/tlv/t166.go
Normal file
12
protocol/tlv/t166.go
Normal file
@ -0,0 +1,12 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T166(imageType byte) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x166)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteByte(imageType)
|
||||
}))
|
||||
})
|
||||
}
|
12
protocol/tlv/t16e.go
Normal file
12
protocol/tlv/t16e.go
Normal file
@ -0,0 +1,12 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T16E(buildModel []byte) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x16e)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.Write(buildModel)
|
||||
}))
|
||||
})
|
||||
}
|
14
protocol/tlv/t177.go
Normal file
14
protocol/tlv/t177.go
Normal file
@ -0,0 +1,14 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T177() []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x177)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteByte(0x01)
|
||||
w.WriteUInt32(1571193922)
|
||||
w.WriteTlv([]byte("6.0.0.2413"))
|
||||
}))
|
||||
})
|
||||
}
|
18
protocol/tlv/t18.go
Normal file
18
protocol/tlv/t18.go
Normal file
@ -0,0 +1,18 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T18(appId uint32, uin uint32) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x18)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(1)
|
||||
w.WriteUInt32(1536)
|
||||
w.WriteUInt32(appId)
|
||||
w.WriteUInt32(0)
|
||||
w.WriteUInt32(uin)
|
||||
w.WriteUInt16(0)
|
||||
w.WriteUInt16(0)
|
||||
}))
|
||||
})
|
||||
}
|
16
protocol/tlv/t187.go
Normal file
16
protocol/tlv/t187.go
Normal file
@ -0,0 +1,16 @@
|
||||
package tlv
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
)
|
||||
|
||||
func T187(macAddress []byte) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x187)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
h := md5.Sum(macAddress)
|
||||
w.Write(h[:])
|
||||
}))
|
||||
})
|
||||
}
|
16
protocol/tlv/t188.go
Normal file
16
protocol/tlv/t188.go
Normal file
@ -0,0 +1,16 @@
|
||||
package tlv
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
)
|
||||
|
||||
func T188(androidId []byte) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x188)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
h := md5.Sum(androidId)
|
||||
w.Write(h[:])
|
||||
}))
|
||||
})
|
||||
}
|
12
protocol/tlv/t191.go
Normal file
12
protocol/tlv/t191.go
Normal file
@ -0,0 +1,12 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T191(k byte) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x191)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteByte(k)
|
||||
}))
|
||||
})
|
||||
}
|
12
protocol/tlv/t194.go
Normal file
12
protocol/tlv/t194.go
Normal file
@ -0,0 +1,12 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T194(imsiMd5 []byte) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x194)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.Write(imsiMd5)
|
||||
}))
|
||||
})
|
||||
}
|
14
protocol/tlv/t2.go
Normal file
14
protocol/tlv/t2.go
Normal file
@ -0,0 +1,14 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T2(result string, sign []byte) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x02)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0)
|
||||
w.WriteStringShort(result)
|
||||
w.WriteTlv(sign)
|
||||
}))
|
||||
})
|
||||
}
|
13
protocol/tlv/t202.go
Normal file
13
protocol/tlv/t202.go
Normal file
@ -0,0 +1,13 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T202(wifiBSSID, wifiSSID []byte) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x202)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteTlvLimitedSize(wifiBSSID, 16)
|
||||
w.WriteTlvLimitedSize(wifiSSID, 32)
|
||||
}))
|
||||
})
|
||||
}
|
52
protocol/tlv/t511.go
Normal file
52
protocol/tlv/t511.go
Normal file
@ -0,0 +1,52 @@
|
||||
package tlv
|
||||
|
||||
import (
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func T511(domains []string) []byte {
|
||||
var arr2 []string
|
||||
for _, d := range domains {
|
||||
if d != "" {
|
||||
arr2 = append(arr2, d)
|
||||
}
|
||||
}
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x511)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(uint16(len(arr2)))
|
||||
for _, d := range arr2 {
|
||||
indexOf := strings.Index(d, "(")
|
||||
indexOf2 := strings.Index(d, ")")
|
||||
if indexOf != 0 || indexOf2 <= 0 {
|
||||
w.WriteByte(0x01)
|
||||
w.WriteTlv([]byte(d))
|
||||
} else {
|
||||
var b byte
|
||||
var z bool
|
||||
i, err := strconv.Atoi(d[indexOf+1 : indexOf2])
|
||||
if err == nil {
|
||||
z2 := (1048576 & i) > 0
|
||||
if (i & 134217728) > 0 {
|
||||
z = true
|
||||
} else {
|
||||
z = false
|
||||
}
|
||||
if z2 {
|
||||
b = 1
|
||||
} else {
|
||||
b = 0
|
||||
}
|
||||
if z {
|
||||
b |= 2
|
||||
}
|
||||
w.WriteByte(b)
|
||||
w.WriteTlv([]byte(d[indexOf2+1:]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
})
|
||||
}
|
12
protocol/tlv/t516.go
Normal file
12
protocol/tlv/t516.go
Normal file
@ -0,0 +1,12 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T516() []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x516)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt32(0)
|
||||
}))
|
||||
})
|
||||
}
|
13
protocol/tlv/t521.go
Normal file
13
protocol/tlv/t521.go
Normal file
@ -0,0 +1,13 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T521() []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x521)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt32(0)
|
||||
w.WriteUInt16(0)
|
||||
}))
|
||||
})
|
||||
}
|
13
protocol/tlv/t525.go
Normal file
13
protocol/tlv/t525.go
Normal file
@ -0,0 +1,13 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T525(t536 []byte) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x525)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(1)
|
||||
w.Write(t536)
|
||||
}))
|
||||
})
|
||||
}
|
12
protocol/tlv/t52d.go
Normal file
12
protocol/tlv/t52d.go
Normal file
@ -0,0 +1,12 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T52D(devInfo []byte) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x52d)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.Write(devInfo)
|
||||
}))
|
||||
})
|
||||
}
|
12
protocol/tlv/t536.go
Normal file
12
protocol/tlv/t536.go
Normal file
@ -0,0 +1,12 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T536(loginExtraData []byte) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x536)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.Write(loginExtraData)
|
||||
}))
|
||||
})
|
||||
}
|
14
protocol/tlv/t8.go
Normal file
14
protocol/tlv/t8.go
Normal file
@ -0,0 +1,14 @@
|
||||
package tlv
|
||||
|
||||
import "github.com/Mrs4s/MiraiGo/binary"
|
||||
|
||||
func T8(localId uint32) []byte {
|
||||
return binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0x8)
|
||||
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||
w.WriteUInt16(0)
|
||||
w.WriteUInt32(localId)
|
||||
w.WriteUInt16(0)
|
||||
}))
|
||||
})
|
||||
}
|
8
protocol/tlv/tlv.go
Normal file
8
protocol/tlv/tlv.go
Normal file
@ -0,0 +1,8 @@
|
||||
package tlv
|
||||
|
||||
func GuidFlag() uint32 {
|
||||
var flag uint32 = 0
|
||||
flag |= 1 << 24 & 0xFF000000
|
||||
flag |= 0 << 8 & 0xFF00
|
||||
return flag
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user