Files
2026-02-17 13:06:23 +08:00

380 lines
14 KiB
Go

package ccdata
import (
"errors"
"hash/crc32"
"strconv"
"time"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/clientsdk/baseutils"
"xiawan/wx/protobuf/wechat"
"github.com/gogo/protobuf/proto"
"github.com/lunny/log"
)
// saeInfo ClientCheckData加密信息
var saeInfo *wechat.SaeInfo
func init() {
initSaeInfo()
}
func initSaeInfo() {
saeBytes, err := baseutils.ReadFile("assets/sae.dat")
if err != nil {
baseutils.PrintLog("initSaeInfo err: " + err.Error())
return
}
saeInfo = &wechat.SaeInfo{}
unmarshalErr := proto.Unmarshal(saeBytes, saeInfo)
if unmarshalErr != nil {
log.Info(unmarshalErr)
}
}
// GetSaeInfo GetSaeInfo
func GetSaeInfo() *wechat.SaeInfo {
return saeInfo
}
// CircleShift CircleShift
func CircleShift(data []byte, offset uint32, pos uint32) []byte {
retData := []byte{}
retData = append(retData, data[0:]...)
if pos == 1 {
retData[offset+0] = data[offset+1]
retData[offset+1] = data[offset+2]
retData[offset+2] = data[offset+3]
retData[offset+3] = data[offset+0]
}
if pos == 2 {
retData[offset+0] = data[offset+2]
retData[offset+2] = data[offset+0]
retData[offset+1] = data[offset+3]
retData[offset+3] = data[offset+1]
}
if pos == 3 {
retData[offset+0] = data[offset+3]
retData[offset+1] = data[offset+0]
retData[offset+2] = data[offset+1]
retData[offset+3] = data[offset+2]
}
return retData
}
// ShiftRows ShiftRows
func ShiftRows(data []byte) []byte {
retData := CircleShift(data, 4, 1)
retData = CircleShift(retData, 8, 2)
retData = CircleShift(retData, 12, 3)
return retData
}
// GetSecTable GetSecTable
func GetSecTable(secTable []byte, encryptRecordData []byte, secTableKey []byte, keyOffset int) []byte {
// fmt.Println("keyOffset = ", keyOffset)
tmpTableKeyOffset := 0
for index := 0; index < 4; index++ {
for secIndex := 0; secIndex < 4; secIndex++ {
tmpOffset := index + secIndex*4
tmpCount := 0
recordIndex := 4*index + secIndex
for threeIndex := 0; threeIndex < 64; threeIndex += 16 {
tmpValue := 4 * int(encryptRecordData[recordIndex])
tmpByte := secTableKey[keyOffset+tmpTableKeyOffset+tmpCount+tmpValue]
secTable[tmpOffset+threeIndex] = tmpByte
tmpCount++
}
tmpTableKeyOffset += 1024
}
}
return secTable
}
// GetSecValue GetSecValue
func GetSecValue(encryptRecordData []byte, secTable []byte, secTableValue []byte, valueOffset int) []byte {
// fmt.Println(secTable)
secTableValueOffset := valueOffset
for index := 0; index < 4; index++ {
for secIndex := 0; secIndex < 4; secIndex++ {
tmpValue := secTable[16*index+4*secIndex+3]
tmpPtrOffset := 16*index + 4*secIndex + 2
outBufferOffset := 4*index + secIndex
for threeIndex := 0; threeIndex < 3; threeIndex++ {
// 第一部分
tmpHigh4Value := (secTable[tmpPtrOffset] & 0xF0) | (tmpValue&0xF0)>>4
tmpValue12 := threeIndex * 0x100
tmpValue14 := byte(secTableValue[secTableValueOffset+int(tmpHigh4Value&0x7F)+0x200-tmpValue12])
if tmpHigh4Value&0x80 == 0 {
tmpValue14 = tmpValue14 & 0x0F
} else {
tmpValue14 = tmpValue14 >> 4
}
// 第二部分
tmpLow4Value := byte(tmpValue&0x0F | 16*secTable[tmpPtrOffset])
tmpValue16 := byte(secTableValue[secTableValueOffset+int(tmpLow4Value&0x7F)+0x280-tmpValue12])
if tmpLow4Value&0x80 == 0 {
tmpValue16 = tmpValue16 & 0x0F
} else {
tmpValue16 = tmpValue16 >> 4
}
// 第三部分
tmpValue = byte((tmpValue14 << 4) | tmpValue16)
tmpPtrOffset = tmpPtrOffset - 1
encryptRecordData[outBufferOffset] = tmpValue
}
secTableValueOffset = secTableValueOffset + 0x300
}
}
return encryptRecordData
}
// GetSecValueFinal GetSecValueFinal
func GetSecValueFinal(encryptRecordData []byte, saeTableFinal []byte) []byte {
for index := 0; index < 4; index++ {
for secIndex := 0; secIndex < 4; secIndex++ {
recordIndex := index*4 + secIndex
recordValue := int(encryptRecordData[recordIndex])
tmpOffset := index*0x400 + secIndex*0x100
tmpValue := saeTableFinal[tmpOffset+recordValue]
encryptRecordData[recordIndex] = tmpValue
}
}
return encryptRecordData
}
// EncodeZipData 加密ClientCheckData压缩后的数据
func EncodeZipData(data []byte, encodeType int) ([]byte, error) {
retBytes := []byte{}
tmpEncodeData := data
saeInfo := GetSaeInfo()
dataLen := len(data)
if saeInfo == nil || dataLen <= 0 {
return retBytes, errors.New("EncodeZipData err: saeInfo == nil || dataLen <= 0")
}
if encodeType != 0x3060 && encodeType != 0x4095 {
return retBytes, errors.New("EncodeZipData err: encodeType != 0x3060 && encodeType != 0x4095")
}
// 先按16字节补齐
lessLen := 16 - dataLen&0xF
if lessLen < 16 {
for index := 0; index < lessLen; index++ {
tmpEncodeData = append(tmpEncodeData, byte(lessLen))
}
}
// IV
ivData := saeInfo.GetIv()
lessEncodeLength := len(tmpEncodeData)
secTable := make([]byte, 64)
// 每次加密16字节
count := lessEncodeLength / 16
for index := 0; index < count; index++ {
tmpOffset := index * 16
outEncodeBuffer := make([]byte, 16)
encryptRecordBuffer := make([]byte, 16)
// 先跟IV异或
for secIndex := 0; secIndex < 16; secIndex++ {
outEncodeBuffer[secIndex] = tmpEncodeData[tmpOffset+secIndex] ^ ivData[secIndex]
}
// 第一次换算
for secIndex := 0; secIndex < 4; secIndex++ {
for threeIndex := 0; threeIndex < 4; threeIndex++ {
encryptRecordBuffer[secIndex*4+threeIndex] = outEncodeBuffer[4*threeIndex+secIndex]
}
}
// 行移位
encryptRecordBuffer = ShiftRows(encryptRecordBuffer)
// 下一步
for secIndex := 0; secIndex < 9; secIndex++ {
// 获取SecTable
if (encodeType & 0x20) == 0x20 {
secTable = GetSecTable(secTable, encryptRecordBuffer, saeInfo.GetTableKey(), secIndex*0x4000)
}
// 获取SecValue
if (encodeType & 0x40) == 0x40 {
encryptRecordBuffer = GetSecValue(encryptRecordBuffer, secTable, saeInfo.GetTableValue(), secIndex*0x3000)
}
encryptRecordBuffer = ShiftRows(encryptRecordBuffer)
}
// 获取最后的SecValue
if (encodeType & 0x1000) == 0x1000 {
encryptRecordBuffer = GetSecValueFinal(encryptRecordBuffer, saeInfo.GetUnknowValue18())
ivData = outEncodeBuffer
for secIndex := 0; secIndex < 4; secIndex++ {
for threeIndex := 0; threeIndex < 4; threeIndex++ {
outEncodeBuffer[secIndex+4*threeIndex] = encryptRecordBuffer[secIndex*4+threeIndex]
}
}
}
// 保存第Index次加密后的16字节数据
retBytes = append(retBytes, outEncodeBuffer[0:]...)
}
return retBytes, nil
}
// GetClientCheckDataInfo GetClientCheckDataInfo
func GetClientCheckDataInfo(deviceInfo *baseinfo.DeviceInfo, ClientVersion uint32) *baseinfo.ClientCheckDataInfo {
if !(int(ClientVersion) > 0) {
ClientVersion = baseinfo.ClientVersion
}
wechatUUID := baseutils.RandomUUID()
retInfo := &baseinfo.ClientCheckDataInfo{}
retInfo.FileSafeAPI = "yes"
retInfo.DylibSafeAPI = "yes"
retInfo.OSVersion = deviceInfo.OsTypeNumber
retInfo.Model = deviceInfo.DeviceName
retInfo.CoreCount = deviceInfo.CoreCount
retInfo.VendorID = baseutils.RandomUUID()
retInfo.ADId = deviceInfo.AdSource
retInfo.NetType = 1
retInfo.IsJaiBreak = 0
retInfo.BundleID = deviceInfo.BundleID
retInfo.Device = deviceInfo.IphoneVer
retInfo.DisplayName = "微信"
retInfo.Version = ClientVersion
retInfo.PListVersion = ClientVersion
retInfo.USBState = 2
retInfo.HasSIMCard = 2
retInfo.LanguageNum = deviceInfo.Language
retInfo.LocalCountry = deviceInfo.RealCountry
retInfo.IsInCalling = 2
retInfo.WechatUUID = "/var/mobile/Containers/Data/Application/" + baseutils.RandomUUID() + "/Documents"
retInfo.APPState = 0
retInfo.IllegalFileList = ""
retInfo.EncryptStatusOfMachO = 1
retInfo.Md5OfMachOHeader = baseinfo.Md5OfMachOHeader
// DirUUID
dirUUID := baseutils.RandomUUID()
retInfo.DylibInfoList = make([]*baseinfo.DylibInfo, 0)
// WeChat
wechatDylibInfo := &baseinfo.DylibInfo{}
wechatDylibInfo.S = "/var/containers/Bundle/Application/" + dirUUID + "/WeChat.app/WeChat"
wechatDylibInfo.U = wechatUUID
retInfo.DylibInfoList = append(retInfo.DylibInfoList, wechatDylibInfo)
// andromeda
andromedaDylibInfo := &baseinfo.DylibInfo{}
andromedaDylibInfo.S = "/private/var/containers/Bundle/Application/" + dirUUID + "/WeChat.app/Frameworks/andromeda.framework/andromeda"
andromedaDylibInfo.U = wechatUUID
retInfo.DylibInfoList = append(retInfo.DylibInfoList, andromedaDylibInfo)
// mars
marsDylibInfo := &baseinfo.DylibInfo{}
marsDylibInfo.S = "/private/var/containers/Bundle/Application/" + dirUUID + "/WeChat.app/Frameworks/mars.framework/mars"
marsDylibInfo.U = wechatUUID
retInfo.DylibInfoList = append(retInfo.DylibInfoList, marsDylibInfo)
// marsbridgenetwork
marsbridgenetworkDylibInfo := &baseinfo.DylibInfo{}
marsbridgenetworkDylibInfo.S = "/private/var/containers/Bundle/Application/" + dirUUID + "/WeChat.app/Frameworks/marsbridgenetworkDylibInfo.framework/marsbridgenetworkDylibInfo"
marsbridgenetworkDylibInfo.U = wechatUUID
retInfo.DylibInfoList = append(retInfo.DylibInfoList, marsbridgenetworkDylibInfo)
// matrixreport
matrixreportDylibInfo := &baseinfo.DylibInfo{}
matrixreportDylibInfo.S = "/private/var/containers/Bundle/Application/" + dirUUID + "/WeChat.app/Frameworks/matrixreport.framework/matrixreport"
matrixreportDylibInfo.U = wechatUUID
retInfo.DylibInfoList = append(retInfo.DylibInfoList, matrixreportDylibInfo)
// OpenSSL
openSSLDylibInfo := &baseinfo.DylibInfo{}
openSSLDylibInfo.S = "/private/var/containers/Bundle/Application/" + dirUUID + "/WeChat.app/Frameworks/OpenSSL.framework/OpenSSL"
openSSLDylibInfo.U = wechatUUID
retInfo.DylibInfoList = append(retInfo.DylibInfoList, openSSLDylibInfo)
// ProtobufLite
protobufLiteDylibInfo := &baseinfo.DylibInfo{}
protobufLiteDylibInfo.S = "/private/var/containers/Bundle/Application/" + dirUUID + "/WeChat.app/Frameworks/ProtobufLite.framework/ProtobufLite"
protobufLiteDylibInfo.U = wechatUUID
retInfo.DylibInfoList = append(retInfo.DylibInfoList, protobufLiteDylibInfo)
return retInfo
}
// CreateClientCheckDataXML 创建ClientCheckDataXML
func CreateClientCheckDataXML(deviceInfo *baseinfo.DeviceInfo, ClientVersion uint32) string {
clientCheckDataInfo := GetClientCheckDataInfo(deviceInfo, ClientVersion)
retString := "<clientCheckData>"
retString = retString + "<fileSafeAPI>" + clientCheckDataInfo.FileSafeAPI + "</fileSafeAPI>"
retString = retString + "<dylibSafeAPI>" + clientCheckDataInfo.DylibSafeAPI + "</dylibSafeAPI>"
retString = retString + "<OSVersion>" + clientCheckDataInfo.OSVersion + "</OSVersion>"
retString = retString + "<table>" + clientCheckDataInfo.Model + "</table>"
retString = retString + "<coreCount>" + strconv.Itoa(int(clientCheckDataInfo.CoreCount)) + "</coreCount>"
retString = retString + "<vendorID>" + clientCheckDataInfo.VendorID + "</vendorID>"
retString = retString + "<adID>" + clientCheckDataInfo.ADId + "</adID>"
retString = retString + "<netType>" + strconv.Itoa(int(clientCheckDataInfo.NetType)) + "</netType>"
retString = retString + "<isJailbreak>" + strconv.Itoa(int(clientCheckDataInfo.IsJaiBreak)) + "</isJailbreak>"
retString = retString + "<bundleID>" + clientCheckDataInfo.BundleID + "</bundleID>"
retString = retString + "<device>" + clientCheckDataInfo.Device + "</device>"
retString = retString + "<displayName>" + clientCheckDataInfo.DisplayName + "</displayName>"
retString = retString + "<version>" + strconv.Itoa(int(clientCheckDataInfo.Version)) + "</version>"
retString = retString + "<plistVersion>" + strconv.Itoa(int(clientCheckDataInfo.PListVersion)) + "</plistVersion>"
retString = retString + "<USBState>" + strconv.Itoa(int(clientCheckDataInfo.USBState)) + "</USBState>"
retString = retString + "<HasSIMCard>" + strconv.Itoa(int(clientCheckDataInfo.HasSIMCard)) + "</HasSIMCard>"
retString = retString + "<languageNum>" + clientCheckDataInfo.LanguageNum + "</languageNum>"
retString = retString + "<localeCountry>" + clientCheckDataInfo.LocalCountry + "</localeCountry>"
retString = retString + "<isInCalling>" + strconv.Itoa(int(clientCheckDataInfo.IsInCalling)) + "</isInCalling>"
retString = retString + "<weChatUUID>" + clientCheckDataInfo.WechatUUID + "</weChatUUID>"
retString = retString + "<AppState>" + strconv.Itoa(int(clientCheckDataInfo.APPState)) + "</AppState>"
retString = retString + "<illegalFileList>" + clientCheckDataInfo.IllegalFileList + "</illegalFileList>"
retString = retString + "<encryptStatusOfMachO>" + strconv.Itoa(int(clientCheckDataInfo.EncryptStatusOfMachO)) + "</encryptStatusOfMachO>"
retString = retString + "<md5OfMachOHeader>" + clientCheckDataInfo.Md5OfMachOHeader + "</md5OfMachOHeader>"
retString = retString + "<dylibInfo>"
count := len(clientCheckDataInfo.DylibInfoList)
for index := 0; index < count; index++ {
dylibInfo := clientCheckDataInfo.DylibInfoList[index]
retString = retString + "<i>"
retString = retString + "<s>"
retString = retString + dylibInfo.S
retString = retString + "</s>"
retString = retString + "<u>"
retString = retString + dylibInfo.U
retString = retString + "</u>"
retString = retString + "</i>"
}
retString = retString + "</dylibInfo>"
retString = retString + "</clientCheckData>"
return retString
}
// CreateClientCheckData 生成ClientCheckData
func CreateClientCheckData(clientCheckDataXML string) ([]byte, error) {
finalXML := clientCheckDataXML
crc32Value := crc32.ChecksumIEEE([]byte(finalXML))
currentTime := int(time.Now().UnixNano() / 1000000000)
finalXML = finalXML + "<ccdcc>" + strconv.Itoa(int(crc32Value)) + "</ccdcc>"
finalXML = finalXML + "<ccdts>" + strconv.Itoa(currentTime) + "</ccdts>"
// 压缩
zipData := baseutils.CompressByteArray([]byte(finalXML))
retData, err := EncodeZipData(zipData, 0x3060)
if err != nil {
return []byte{}, err
}
return retData, nil
}