package fqapi import ( "crypto/rand" "encoding/hex" "encoding/json" "fanqie/internal/common" "fanqie/internal/config" "fmt" "math/big" "net" "os" "runtime" "sort" "strconv" "strings" "time" "github.com/google/uuid" ) // 使用共享的设备配置 var ( androidVersions = DeviceConfig.AndroidVersions brandModels = DeviceConfig.BrandModels deviceBrands = DeviceConfig.DeviceBrands ) // generateUUID 生成UUID4字符串 func generateUUID() string { return uuid.New().String() } // currentMillis 返回当前毫秒级时间戳 func currentMillis() int64 { return time.Now().UnixNano() / int64(time.Millisecond) } // getCustomBT 获取设备开机时间戳 // 模拟 Android 中的 m25453o() 方法,计算设备开机时间戳: // custom_bt = current_millis - elapsed_ms_since_boot func getCustomBT() string { var uptimeSeconds float64 if runtime.GOOS == "linux" { // 在Linux上读取/proc/uptime data, err := os.ReadFile("/proc/uptime") if err == nil { // /proc/uptime格式: "系统运行时间(秒) 空闲时间(秒)" parts := strings.Split(string(data), " ") if len(parts) > 0 { uptime, err := strconv.ParseFloat(strings.TrimSpace(parts[0]), 64) if err == nil { uptimeSeconds = uptime } } } // 如果读取失败,使用time.monotonic作为备选 if uptimeSeconds == 0 { // Go没有直接的monotonic API,使用启动时间估算 uptimeSeconds = float64(time.Now().Unix() % 86400) } } else { // 非Linux系统,使用估算值 // 这里可以使用进程启动时间作为近似 uptimeSeconds = float64(time.Now().Unix() % 86400) } return fmt.Sprintf("%d", currentMillis()-int64(uptimeSeconds*1000)) } // generateDeviceInfo 随机生成设备及操作系统信息 func generateDeviceInfo() map[string]interface{} { osVersion := androidVersions[randInt(len(androidVersions))] // 随机选择品牌 deviceBrand := deviceBrands[randInt(len(deviceBrands))] // 根据品牌选择对应的型号 models := brandModels[deviceBrand] deviceModel := models[randInt(len(models))] releaseBytes := make([]byte, 7) rand.Read(releaseBytes) releaseBuild := hex.EncodeToString(releaseBytes) openUDIDBytes := make([]byte, 10) rand.Read(openUDIDBytes) return map[string]interface{}{ "os": "Android", "os_version": osVersion[0], "os_api": osVersion[1], "device_model": deviceModel, "device_brand": deviceBrand, "device_manufacturer": deviceBrand, "cpu_abi": "arm64-v8a", "release_build": releaseBuild, "cdid": generateUUID(), "sig_hash": "a4a27c2633195374c15651ffc3c4a497", "openudid": hex.EncodeToString(openUDIDBytes), "clientudid": generateUUID(), "req_id": generateUUID(), } } // randInt 生成随机整数 func randInt(max int) int { n, _ := rand.Int(rand.Reader, big.NewInt(int64(max))) return int(n.Int64()) } // randomString 生成随机字符串 func randomString(length int) string { const charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" b := make([]byte, length) for i := range b { n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(charset)))) b[i] = charset[n.Int64()] } return string(b) } // generateOptionalFields 生成ROM信息等 func generateOptionalFields() (map[string]interface{}, map[string]int64) { rom := randomString(14) part1 := randomString(4) part2 := randomString(6) part3 := randomString(8) genTime := currentMillis() return map[string]interface{}{ "rom": rom, "rom_version": fmt.Sprintf("coloros__%s.%s.%s release-keys", part1, part2, part3), "apk_first_install_time": genTime - int64(randInt(365))*86400000, }, map[string]int64{ "_gen_time": genTime, } } // generateIPv6AddressLinkLocal 生成链路本地IPv6地址 func generateIPv6AddressLinkLocal() string { ifaces, err := net.Interfaces() if err != nil { return "::1" } for _, iface := range ifaces { addrs, err := iface.Addrs() if err != nil { continue } for _, addr := range addrs { ipNet, ok := addr.(*net.IPNet) if !ok { continue } ip := ipNet.IP if ip.To16() != nil && strings.HasPrefix(ip.String(), "fe80::") { return strings.Split(ip.String(), "%")[0] } } } return "::1" } // generateIPv6AddressUniqueLocal 生成唯一本地IPv6地址 func generateIPv6AddressUniqueLocal() string { b := make([]byte, 1) rand.Read(b) parts := []string{fmt.Sprintf("fd%02x", b[0])} for i := 0; i < 7; i++ { b := make([]byte, 2) rand.Read(b) parts = append(parts, fmt.Sprintf("%04x", uint16(b[0])<<8|uint16(b[1]))) } return strings.Join(parts, ":") } // generateIPv6List 生成IPv6列表 func generateIPv6List() []map[string]string { return []map[string]string{ {"type": "client_anpi", "value": generateIPv6AddressLinkLocal()}, {"type": "client_anpi", "value": generateIPv6AddressUniqueLocal()}, {"type": "client_anpi", "value": generateIPv6AddressUniqueLocal()}, } } // deviceIdentity 注册设备身份:pad(默认,历史行为)/ phone(对齐手机 App 书源注册 payload) func deviceIdentity() string { if cfg := config.GetConfig(); cfg != nil && cfg.Fanqie.DeviceIdentity == "phone" { return "phone" } return "pad" } // deviceCustomFields 注册身份对应的 custom 字段 func deviceCustomFields() map[string]interface{} { if deviceIdentity() == "phone" { return map[string]interface{}{"host_bit": 64, "dragon_device_type": "phone"} } return map[string]interface{}{"host_bit": 32, "dragon_device_type": "pad"} } // GenerateFullRequestBody 生成完整的请求体数据 func GenerateFullRequestBody() map[string]interface{} { rawData := map[string]interface{}{ "magic_tag": "ss_app_log", "header": map[string]interface{}{ "display_name": "番茄免费小说", "aid": 1967, "channel": "43536163a", "package": "com.dragon.read", "sdk_version": "3.7.0-rc.25-fanqie-xiaoshuo", "sdk_target_version": 29, "git_hash": "711d1a7", "density_dpi": 240, "display_density": "hdpi", "resolution": "720x1280", "language": "zh", "timezone": 8, "access": "wifi", "not_request_sender": 0, "carrier": "CHINA MOBILE", "mcc_mnc": "46000", "region": "CN", "tz_name": "Asia/Shanghai", "tz_offset": 28800, "sim_region": "cn", "sim_serial_number": []interface{}{}, "oaid_may_support": false, "device_platform": "android", "custom": deviceCustomFields(), "pre_installed_channel": "", "is_system_app": 0, "sdk_flavor": "china", "guest_mode": 0, }, } deviceInfo := generateDeviceInfo() optionalFields, genTime := generateOptionalFields() header := rawData["header"].(map[string]interface{}) for k, v := range deviceInfo { header[k] = v } for k, v := range optionalFields { header[k] = v } header["ipv6_list"] = generateIPv6List() // phone 身份补充真机指纹字段(对齐书源注册 payload,规避 pad 身份的差异化风控) if deviceIdentity() == "phone" { rom, _ := header["rom"].(string) header["hardware"] = "qcom" header["device_device"] = rom header["fingerprint"] = fmt.Sprintf("%v/%v/%v:%v/%s/%s:user/release-keys", header["device_brand"], header["device_model"], header["device_model"], header["os_version"], rom, rom) } for k, v := range genTime { rawData[k] = v } return rawData } // tryActivatePremium 尝试激活设备特权 func tryActivatePremium(header string) { logger := common.NewLogger("device_register", "INFO", "") // 构建查询参数 baseParams := []string{ "aid=1967", "app_name=novelapp", "channel=0", "device_platform=android", "os_version=0", "version_code=" + ClientVersionCode, "manifest_version_code=" + ClientVersionCode, "update_version_code=" + ClientVersionCode, header, } // 合并并排序参数 allParams := strings.Join(baseParams, "&") paramList := strings.Split(allParams, "&") sort.Strings(paramList) sortedParams := strings.Join(paramList, "&") requestBody := map[string]interface{}{ "add_count_daily": 0, "amount": 2592000, "privilege_id": 7210376203117531962, "from": 8, "unique_key": fmt.Sprintf("%d", time.Now().UnixNano()/1e6), } bodyJSON, _ := json.Marshal(requestBody) url := "https://reading.snssdk.com/reading/user/privilege/add/v/?" + sortedParams resp, err := GorgonRequestSync(url, map[string]interface{}{ "method": "post", "headers": map[string]string{ "content-type": "application/json", }, "body": bodyJSON, }) if err != nil { logger.Error("激活特权失败: " + err.Error()) return } var result map[string]interface{} json.Unmarshal(resp.Content, &result) if code, ok := result["code"].(float64); ok && code == 0 { logger.Info("已成功激活设备特权") } else { logger.Warning("设备特权激活失败...") } } // Register 设备注册 func Register() (string, error) { logger := common.NewLogger("device_register", "INFO", "") logger.Info("正在注册设备...") // 生成完整请求体数据 data := GenerateFullRequestBody() // 只记录关键设备信息 header := data["header"].(map[string]interface{}) logger.Info(fmt.Sprintf("生成设备信息 - 品牌: %s, 型号: %s, 系统: Android %v", header["device_brand"], header["device_model"], header["os_version"])) jsonData, err := json.Marshal(data) if err != nil { logger.Error("JSON序列化失败: " + err.Error()) return "", err } // 使用TTEncrypt加密 encrypted, err := TTEncrypt(jsonData) if err != nil { logger.Error("加密失败: " + err.Error()) return "", err } url := "https://i.snssdk.com/service/2/device_register/?tt_data=a" headers := map[string]string{ "user-agent": "okhttp/4.10.0", "content-type": "application/octet-stream; tt-data=a", } client := common.NewHTTPClient() resp, err := client.Post(url, encrypted, headers) if err != nil { logger.Warning("设备注册失败!") logger.Warning(err.Error()) return "", fmt.Errorf("设备注册错误") } var respData map[string]interface{} err = json.Unmarshal(resp.Content, &respData) if err != nil { logger.Error("响应解析失败: " + err.Error()) return "", err } if resp.Status == 200 { // 检查device_id deviceID, ok := respData["device_id"] if !ok { logger.Warning("设备注册失败!响应中没有device_id") return "", fmt.Errorf("设备注册失败") } // 转换device_id为float64并检查 deviceIDFloat, ok := deviceID.(float64) if !ok || deviceIDFloat == 0 { logger.Warning("设备注册失败!device_id无效") return "", fmt.Errorf("设备注册失败") } deviceModel := data["header"].(map[string]interface{})["device_model"].(string) header := fmt.Sprintf("device_id=%v&device_type=%s&iid=%v&version_name="+ClientVersionName, respData["device_id_str"], deviceModel, respData["install_id_str"]) logger.Info("设备注册成功!") // 尝试激活特权 tryActivatePremium(header) return header, nil } logger.Warning("设备注册失败!") return "", fmt.Errorf("设备注册失败") }