01 · 概述与目标#
目标样本:com.gof.china_1.15.6_und3fined.ipa
分析对象:Payload/whiteoutsurvivalcn.app(已脱壳 / decrypted)
报告版本:v1.0
分析性质:授权安全研究 / 移动应用安全评估(见文末免责声明)
0. 报告导读(TL;DR)#
com.gof.china 1.15.6 (build 135)cryptid=0),Payload/decrypt.day = "und3fined"global-metadata.dat 使用 固定 17 字节周期密钥 NTESCSLLVMPASSKEY 做整文件 XOR 加密,已成功还原(version=31, 8511 个类型、19294 条字符串字面量全部可读);密钥前缀 NTES + 元数据中的 NetEase.NetSecProtect 符号,指向 网易易盾 NetSec 加固CAB- 名)明文可读,但压缩块数据被加密(flags=0x0243 含 kArchiveUsesAssetBundleEncryption;实测 UnityPy 仅在块数据解压处失败),疑似逐包密钥 XOR(CalcEncryptKeyFromString/BundleEncryptOffset)luapackage_32/64、toluaout_32/64_*.unity3d,各 ~20MB),运行时由 Helper.DecryptLuaBytecode 解密执行 → 跳过首个加密块后已还原 53MB Lua 逻辑文本AntiCheatResult、isJailbreak、isDebuggerAttached、SC_DATA_TAMPERED)CGPushServerExtension 声明 NSAllowsArbitraryLoads=true(明文 HTTP 放行)oauth2: 引用),构成供应链信息泄露1. 目标标识(Host Identification)#
| 项 | 值 |
|---|---|
| IPA 文件名 | com.gof.china_1.15.6_und3fined.ipa |
| IPA 大小 / SHA-256 | 754,433,203 B (721 MiB) / 1e64c47197def718e28f5ed0acd4aeba03bcfe497bf6b3284ff086d060a8b3aa |
| 显示名称 | 无尽冬日(CFBundleDisplayName) |
| Bundle ID | com.gof.china |
| 版本 / 构建号 | 1.15.6 / 135 |
| 应用目录 | Payload/whiteoutsurvivalcn.app |
| 最低系统 | iOS 12.0(MinimumOSVersion) |
| 构建 SDK | iphoneos 26.1(Xcode 26.1 / DTXcodeBuild 17B100) |
| 签名团队 | Team ID HM5Y58WVBP,application-identifier = HM5Y58WVBP.com.gof.china |
| 引擎 | Unity 2022.3.52f1(game_res_mark 头 "UnityFS … 2022.3.52f1") |
| 脚本后端 | IL2CPP(Data/Managed/Metadata/global-metadata.dat 存在) |
| 自研 SDK | dd_sdk_unity_version = 8.0.5.0-GOFCN-1(Century Games 自研 SDK) |
| 主可执行文件 | whiteoutsurvivalcn(arm64, 138,720 B, SHA-256 e90956ef…62336) |
| Unity 主库 | Frameworks/UnityFramework.framework/UnityFramework(arm64, 77,587,120 B, SHA-256 d7262bd5…a93658) |
说明:Payload/decrypt.day 内容为 und3fined,配合脱壳工具使用;主二进制与 UnityFramework 的 LC_ENCRYPTION_INFO_64.cryptid 均为 0,即代码段已解密,可直接进行静态分析。
2. 包结构总览#
脱壳后的 whiteoutsurvivalcn.app 共 6,218 个文件 / 约 901 MB(含分析过程中生成的中间文件)。
whiteoutsurvivalcn.app/
├── whiteoutsurvivalcn 主二进制 (arm64, 138 KB, 仅做 Unity/SDK 引导)
├── Info.plist 应用配置(权限/URL Scheme/ATS 等)
├── PrivacyInfo.xcprivacy 隐私清单
├── ddCommon.json ★ 自研 SDK 配置(含可逆编码的密钥/密钥串)
├── UOPSDKConfig.json 字节 UnionOpenPlatform 配置(app_id 789841)
├── Assets.car 2.3 MB(App 图标等资源)
├── CenturyGameResources.bundle/ 自研 SDK 资源(含 merge 脚本、mopub SKAdNetwork 清单)
├── CenturyGameiOSAccount/Payment/… 自研 SDK 各功能 bundle
├── CGSQRCode.bundle / CenturyGameSmartHelpRes.bundle
├── Frameworks/ 100 MB,15 个内嵌框架 + 12 个 Swift 运行库 dylib
│ ├── UnityFramework.framework/ ★ Unity 引擎 + IL2CPP + 游戏原生代码
│ ├── AdjustSdk / AdjustSigSdk Adjust 归因
│ ├── ThinkingSDK / ThinkingDataCore 数数 ThinkingData 埋点
│ ├── BDASignalSDK 字节信号 SDK
│ ├── CrashSight* (Core/Adapter/Plugin) 腾讯 CrashSight 崩溃捕获
│ ├── UnionOpenPlatform* 字节抖音开放平台(登录/分享/归因)
│ ├── AFNetworking / Protobuf 网络 / 序列化
│ ├── FirebaseNIMessInstallation Firebase 安装归因
│ └── libswift*.dylib Swift 运行库
├── PlugIns/CGPushServerExtension.appex/ ★ 通知服务扩展(NSAllowsArbitraryLoads=true)
├── Data/
│ ├── Managed/Metadata/global-metadata.dat ★ IL2CPP 元数据(XOR 加密)
│ ├── Raw/data/ 715 MB,5,316 个资源包子目录/文件(含 luapackage_32/64)
│ ├── Raw/sound/ CriWare .acb 音频
│ ├── Raw/world/ 世界地图 .bytes 配置
│ └── (globalgamemanagers / level0-2 / resources.assets 等)
├── SC_Info/ FairPlay 签名信息(whiteoutsurvivalcn.sinf 等)
├── install/ ★ 残留构建脚本(buildIpa.sh / autoUpdateRepoPublic.sh)
└── _CodeSignature/CodeSignature
3. 分析方法与工具链#
| 阶段 | 方法 / 工具 | 产出 |
|---|---|---|
| 解包 | unzip(IPA 为 store 压缩) |
完整 app 目录 |
| 加壳确认 | otool -l(LC_ENCRYPTION_INFO_64) |
cryptid=0,已脱壳 |
| 架构 | file / lipo / otool -h |
arm64 单架构,MH_PIE |
| 静态串 | strings / nm / otool -L / dyld_info |
SDK、反调试、网络端点线索 |
| 配置 | plutil / codesign -d --entitlements / Python JSON |
权限、entitlements、密钥配置 |
| IL2CPP 还原 | 自研 Python 解析器(自相关求密钥周期 → 众数恢复密钥 → 结构解析) | 类型 8,511 / 字符串字面量 19,294 / 标识符 88,084 |
| 资源格式 | 十六进制头分析 + 元数据方法名交叉验证 | 资源包 XOR 加密结论 |
关键突破点:global-metadata.dat 的周期 XOR。详见第 6 章与附录脚本 artifacts/decrypt_global_metadata.py。
4. 报告结构#
| 章节 | 内容 | 文件 |
|---|---|---|
| 01 | 概述、目标标识、包结构与总体结论 | 本文件 |
| 02 | 第三方 SDK 清单、权限与隐私分析 | report/02_sdk_privacy.md |
| 03 | Unity 资源、资源包加密与内容分析 | report/03_unity_assets.md |
| 04 | 二进制结构与反篡改 / 反调试机制 | report/04_binary_antitamper.md |
| 05 | 网络端点、配置与硬编码密钥 | report/05_network_secrets.md |
| 06 | IL2CPP 元数据加密还原(核心技术章节) | report/06_il2cpp_metadata.md |
| 07 | 风险汇总、攻击面与加固建议 | report/07_risk_remediation.md |
| 08 | 内购 / 充值系统逆向(支付 SDK、协议、配置) | report/08_iap.md |
| 09 | 玩法 / 系统架构与网络协议地图 | report/09_gameplay.md |
| 10 | 数值 / 配表体系与属性公式 | report/10_numeric.md |
| 11 | 活动 / 节日运营与活动模型 | report/11_events.md |
| 附录 | 解密脚本与可复现命令 | report/appendix.md |
5. 免责声明#
本报告仅用于经授权的安全研究、兼容性评估与防御加固。所涉脱壳、元数据还原、串提取等均为离线静态分析,未对任何线上服务发起攻击、未绕过任何付费/授权,也未分发任何受版权保护的内容。请遵守当地法律法规与软件许可协议,勿将本文技术用于未授权用途。
02. 第三方 SDK 清单、权限与隐私画像#
分析对象:无尽冬日(Whiteout Survival CN)
包名(Bundle ID):com.gof.china
版本:CFBundleShortVersionString = 1.15.6(CFBundleVersion = 135)
构建工具链:Xcode 26.1(DTXcode = 2611 / DTXcodeBuild = 17B100),SDK iphoneos26.1
产物路径:extracted/Payload/whiteoutsurvivalcn.app(已解密 IPA,只读分析,未做修改)
主二进制 SHA-256:e90956ef435b67fba08ead8064778d349520179980423efc07705a9a99c62336
签名:Apple iPhone OS Application Signing,TeamIdentifier HM5Y58WVBP,Format=app bundle with Mach-O thin (arm64)
分析方法:plutil / PlistBuddy 解析各 Info.plist、codesign -d --entitlements :- 提取授权、strings 提取符号、find / grep / python3 解析资源 JSON 与隐私清单。所有版本号、标识符均逐字引自文件,未作推测;无法验证处已明确标注。
1. 嵌入式框架(Framework)完整清单#
Frameworks/ 下共有 15 个 .framework,另有 13 个 Swift 运行库动态库(libswiftCore.dylib 等,属 Unity 打包产物,非第三方 SDK)。全部版本信息读取自各框架自身的 Info.plist(CFBundleShortVersionString / CFBundleIdentifier)。
| # | Framework | CFBundleIdentifier | 版本 | 分类 | 作用与风险备注 |
|---|---|---|---|---|---|
| 1 | UnityFramework.framework |
com.unity3d.framework |
1.15.6 (135) | 游戏引擎 | Unity IL2CPP 运行时(79 MB),承载全部游戏逻辑与原生桥接;攻击面最大,需关注 UnitySendMessage 等 JSBridge 通道。 |
| 2 | AdjustSdk.framework |
org.cocoapods.AdjustSdk |
5.4.1 | 归因/分析 | Adjust 安装与事件归因 SDK;声明 NSPrivacyTracking=true,会采集 DeviceID 用于追踪与第三方广告,是本 App 最核心的广告追踪组件。 |
| 3 | AdjustSigSdk.framework |
com.adjust.sdk.sig |
1.0 | 归因/分析 | Adjust 签名 SDK(ADJSigner,符号 sign:withActivityKind:withSdkVersion:),对归因参数做签名校验;采集 DeviceID / 诊断数据,需联网。 |
| 4 | ThinkingDataCore.framework |
org.cocoapods.ThinkingDataCore |
1.0.2 | 分析 | 数数科技(ThinkingData)埋点内核;自身隐私清单为空,实际能力由 ThinkingSDK 承载。 |
| 5 | ThinkingSDK.framework |
org.cocoapods.ThinkingSDK |
3.0.6 | 分析 | 数数科技事件分析 SDK;采集产品交互、崩溃、性能、粗略位置(CoarseLocation)、UserID、DeviceID,用途 Analytics。 |
| 6 | BDASignalSDK.framework |
org.cocoapods.BDASignalSDK |
1.0.1 | 归因/分析 | 字节跳动 BDA Signal SDK(设备信号采集,配合 UOP 归因);访问磁盘空间、文件时间戳、UserDefaults。 |
| 7 | CrashSight.framework |
com.cs.CrashSight |
1.0 | 崩溃上报 | 腾讯 CrashSight(原 Bugly)崩溃采集主 SDK;符号含 CSCrashSightManager / CrashSightDelegate,采集崩溃、性能、DeviceID、UserID(不关联用户)。 |
| 8 | CrashSightCore.framework |
com.uqm.CrashSightCore |
1.0 | 崩溃上报 | CrashSight 核心实现层。 |
| 9 | CrashSightAdapter.framework |
com.uqm.CrashSightAdapter |
1.0 | 崩溃上报 | CrashSight 与宿主/Unity 的适配层。 |
| 10 | CrashSightPlugin.framework |
com.uqm.CrashSightPlugin |
1.0 | 崩溃上报 | CrashSight Unity 插件封装。 |
| 11 | UnionOpenPlatformCore.framework |
ByteDance.UnionOpenPlatformCore |
25.0.0 | 广告/归因 | 字节跳动「融合开放平台(UOP)/ 抖音开放平台」核心,内嵌 RangersAppLog、RangersAPM、APMInsightCore、HMDEmbed 等子 bundle;唯一显式 NSPrivacyTracking=true 并列出追踪域名的框架(见 §3)。 |
| 12 | UnionOpenPlatformDataLink.framework |
ByteDance.UnionOpenPlatformDataLink |
25.0.0 | 广告/归因 | 字节 UOP 数据链路(归因/链路拉活);采集 GameplayContent、UserID。 |
| 13 | AFNetworking.framework |
com.alamofire.AFNetworking |
4.0.1 | 网络 | 通用 HTTP 网络库,被多数 Objective-C SDK 依赖;本身无独立隐私声明。 |
| 14 | Protobuf.framework |
org.cocoapods.Protobuf |
3.29.5 | 序列化 | Google Protocol Buffers 3.29.5,用于协议编解码;隐私清单为空(无采集)。 |
| 15 | FirebaseNIMessInstallation.framework |
com.google.FirebaseNIMessInstallation |
1.0 | 推送 | 基于 Google Firebase Installations 的封装(PROJECT:FirebaseNIMessInstallation-1),为推送提供 Installation ID/FID 能力。MinimumOSVersion = 9.0。 |
备注:CFBundleShortVersionString 中 Adjustment/ThinkingData 等 CocoaPods 框架版本为 Pod 版本号,能对应到上游发布版本;CrashSight* 与 UnionOpenPlatform* 的版本号为 SDK 内自填(CrashSight 均为 1.0、UOP 为 25.0.0),无法从产物进一步验证其内部小版本,已如实标注。
1.1 次要/内嵌组件(由框架携带,非独立 framework 目录)
UnionOpenPlatformCore.framework/内嵌:RangersAppLog.bundle(字节 AppLog 埋点)、APMInsightCore.bundle与RangersAPMPrivacyInfo.bundle(字节 APM 性能监控)、HMDEmbedPrivacyInfo.bundle(火山引擎 HMD 崩溃嵌入)。- Unity 工程同时打包了
CenturyGameiOS*系列 bundle:CenturyGameiOSAccount(账号)、CenturyGameiOSPayment(支付)、CenturyGameiOSReport(上报)、CenturyGameiOSTool、CenturyGameiOSWebViewBridge(WebView 桥)、CGSQRCode、CenturyGameSmartHelpRes、CenturyGameResources。均为自研/OEM 模块,其隐私清单见 §3。
2. Info.plist 权限与声明#
2.1 主 App(com.gof.china)
NS*UsageDescription(隐私用途说明)
| Key | 值(逐字) | 评估 |
|---|---|---|
NSUserTrackingUsageDescription |
Your data will be used to deliver personalized ads to you |
ATT 追踪弹窗文案,配合 Adjust/UOP 归因使用。 |
NSPhotoLibraryUsageDescription |
We want to use photo for changing your avatar |
相册(更换头像)。 |
NSCameraUsageDescription |
We want to use camera for changing your avatar |
相机(更换头像)。 |
NSMicrophoneUsageDescription |
我们需要使用麦克风以用于客服服务 |
麦克风(客服语音)。 |
NSCalendarsUsageDescription |
我们需要使用日历以用于活动提醒 |
日历读取(活动提醒)。 |
NSCalendarsWriteOnlyAccessUsageDescription |
我们需要使用日历以用于活动提醒 |
日历(仅写入)iOS 17+ 权限。 |
NSLocationWhenInUseUsageDescription |
empty |
⚠️ 占位/未填写:声明了定位能力但文案为 empty,为明显的合规瑕疵(若真调用定位会触发审核风险;也可能仅被 SDK 声明而未实际使用)。 |
注意:应用仅声明了 NSLocationWhenInUseUsageDescription,未声明 NSLocationAlwaysAndWhenInUseUsageDescription,即不支持后台持续定位。
LSApplicationQueriesSchemes(可探测的第三方 App Scheme)
fb, fb-messenger, whatsapp, weixin, weixinULAPI, weixinURLParamsAPI,
douyinopensdk, douyinliteopensdk, douyinsharesdk, snssdk1128, weibosdk
覆盖 Facebook、WhatsApp、微信(weixin / weixinULAPI / weixinURLParamsAPI)、抖音(douyinopensdk 等)、微博。说明 SDK 具备跨 App 拉活/分享能力(canOpenURL 探测)。
CFBundleURLTypes(自定义 URL Scheme 注册)
| CFBundleURLName | Scheme | 说明 |
|---|---|---|
centurygame |
com.diandian.logger.20173 |
点点数据/自研日志回传(20173 = 游戏 ID)。 |
cgsdk |
ddplatform-20173 |
CenturyGame SDK 平台回调。 |
bytedanceAttribution |
com.gof.china |
⚠️ 把主 Bundle ID 本身注册为 URL Scheme,用于字节归因回跳。 |
weixin |
wx32374c9038cf9759 |
微信开放平台 AppID。 |
douyin |
awebivvetg90lv93 |
抖音开放平台 client_key。 |
| (空 dict) | — | 存在一个空 URL Type(打包瑕疵)。 |
UIBackgroundModes
remote-notification
仅声明远程推送后台模式,未申请 location / audio / fetch 等。
其他关键键
NSAdvertisingAttributionReportEndpoint = "https://adjust-skadnetwork.com\u200b"—— ⚠️ 末尾含不可见零宽空格(U+200B,0x200B),共 31 字符。这不是合法可解析的目标域名,存在被系统忽略或配置失效的风险,属打包/配置瑕疵,建议修正为纯 ASCII URL。UIRequiresFullScreen = true、UIRequiresPersistentWiFi = true、MinimumOSVersion = 12.0、UIDeviceFamily = [1,2](iPhone/iPad)。- Unity 推送相关:
UnityNotificationDefaultAuthorizationOptions = 7、UnityAddRemoteNotificationCapability = false、UnityUseAPSReleaseEnvironment = false。 dd_sdk_environment_production = true、dd_sdk_unity_version = "8.0.5.0-GOFCN-1"(数数 SDK 生产环境)。
Entitlements(codesign -d --entitlements :- whiteoutsurvivalcn)
application-identifier = HM5Y58WVBP.com.gof.china
aps-environment = production
com.apple.developer.applesignin = [Default]
com.apple.developer.associated-domains = [
applinks:appleunilink.campfiregames.cn,
applinks:xfmr.adj.st
]
com.apple.developer.game-center = true
com.apple.developer.team-identifier = HM5Y58WVBP
2.2 推送扩展 PlugIns/CGPushServerExtension.appex
二进制 Info.plist(已 plutil -convert xml1 解析):
| Key | 值 |
|---|---|
CFBundleIdentifier |
com.gof.china.20173 |
CFBundleShortVersionString / CFBundleVersion |
1.15.6 / 135 |
CFBundleDisplayName |
NotificationServer |
NSExtensionPointIdentifier |
com.apple.usernotifications.service |
NSExtensionPrincipalClass |
NotificationService |
NSExtension |
通知服务扩展(可修改推送内容/图片) |
MinimumOSVersion |
10.0 |
UIRequiredDeviceCapabilities |
arm64 |
NSAppTransportSecurity |
NSAllowsArbitraryLoads = true ⚠️ |
无 NS*UsageDescription / UIBackgroundModes / CFBundleURLTypes |
— |
Appex Entitlements:
application-identifier = HM5Y58WVBP.com.gof.china.20173
com.apple.developer.team-identifier = HM5Y58WVBP
扩展本身只持有 application-identifier 与 team-id,未继承主 App 的 aps/关联域名等权益,权限相对收敛。
3. 隐私清单(PrivacyInfo.xcprivacy)汇总#
产物内共发现 22 个 PrivacyInfo.xcprivacy。以下按主 App、Appex、框架三类归纳。
3.1 主 App 隐私清单(PrivacyInfo.xcprivacy)
NSPrivacyAccessedAPITypes(Required Reason API)
| 分类 | 理由码 |
|------|--------|
| NSPrivacyAccessedAPICategorySystemBootTime | 35F9.1 |
| NSPrivacyAccessedAPICategoryDiskSpace | E174.1 |
| NSPrivacyAccessedAPICategoryUserDefaults | CA92.1 |
| NSPrivacyAccessedAPICategoryFileTimestamp | 0A2A.1, C617.1 |
NSPrivacyCollectedDataTypes
| 数据类型 | Linked | Tracking | 用途 |
|----------|--------|----------|------|
| NSPrivacyCollectedDataTypeDeviceID | true | false | Analytics, Other |
| NSPrivacyCollectedDataTypePhotosorVideos | true | false | AppFunctionality |
NSPrivacyTracking = false(主 App 清单自述不追踪,但与 Adjust / UOP 框架清单中 NSPrivacyTracking=true 存在聚合层面的矛盾,见 §7)。
3.2 Appex 隐私清单(CGPushServerExtension.appex/PrivacyInfo.xcprivacy)
NSPrivacyAccessedAPITypes:仅NSPrivacyAccessedAPICategoryUserDefaults(CA92.1)。NSPrivacyCollectedDataTypes:空。NSPrivacyTracking = false,NSPrivacyTrackingDomains = []。
3.3 各框架隐私清单要点
| Framework / Bundle | Tracking | 被声明采集的数据类型 | Required Reason API |
|---|---|---|---|
AdjustSdk(Adjust.bundle) |
true | DeviceID(Linked,Analytics + ThirdPartyAdvertising,Tracking=true)、ProductInteraction | UserDefaults(C A92.1)、FileTimestamp(C617.1) |
AdjustSigSdk |
false | OtherDiagnosticData、DeviceID | — |
ThinkingSDK |
false | ProductInteraction、CrashData、PerformanceData、CoarseLocation、UserID、DeviceID | UserDefaults(1C8F.1)、DiskSpace(E174.1,7D9E.1)、SystemBootTime(35F9.1)、FileTimestamp(C617.1) |
ThinkingDataCore |
false | 空 | — |
BDASignalSDK |
(未声明 keys) | 空 | DiskSpace(E174.1)、FileTimestamp(DDA9.1)、UserDefaults(CA92.1) |
CrashSight |
(无 tracking 键) | PerformanceData、CrashData、DeviceID、UserID | UserDefaults(CA92.1)、DiskSpace(E174.1)、FileTimestamp(C617.1)、SystemBootTime(35F9.1) |
UnionOpenPlatformCore |
true | DeviceID、CrashData、PerformanceData、OtherDiagnosticData | FileTimestamp(C617.1,DDA9.1)、SystemBootTime(35F9.1)、DiskSpace(E174.1)、UserDefaults(1C8F.1,CA92.1) |
UnionOpenPlatformCore/RangersAPM |
false | OtherDiagnosticData、PerformanceData、CrashData、UserID、DeviceID | UserDefaults、DiskSpace、FileTimestamp、SystemBootTime |
UnionOpenPlatformCore/HMDEmbed |
false | OtherDiagnosticData、PerformanceData、CrashData、UserID、DeviceID | DiskSpace、FileTimestamp |
UnionOpenPlatformDataLink |
false | GameplayContent、UserID | UserDefaults(CA92.1) |
Protobuf |
false | 空 | — |
UnityFramework 及 CenturyGameiOS* |
false | DeviceID、PhotosorVideos(Account 额外含 Emails/UserID;Payment 含 PurchaseHistory;Report 含 PerformanceData/OtherUsageData) | SystemBootTime、DiskSpace、UserDefaults、FileTimestamp |
UnionOpenPlatformCore 显式列出的追踪域名(NSPrivacyTrackingDomains):
klink-tracking.volceapplog.com
toblog-tracking.volceapplog.com
abtest-tracking.volceapplog.com
tobapplog-tracking.volceapplog.com
alink-tracking.volceapplog.com
AdjustSdk 显式列出的追踪域名:
https://consent.adjust.com https://consent.adjust.io https://consent.adjust.net.in
https://consent.adjust.world https://consent.adjust.cn https://consent.eu.adjust.com
https://consent.tr.adjust.com https://consent.us.adjust.com
4. ⚠️ 关键发现:Appex 开启 ATS 任意加载,主 App 未开启#
- 主 App(
com.gof.china)没有NSAppTransportSecurity键(PlistBuddy Print :NSAppTransportSecurity返回Does Not Exist),即主 App 遵循系统默认 ATS(强制 HTTPS/TLS)。 - 推送扩展
CGPushServerExtension.appex(com.gof.china.20173)在Info.plist中显式声明:xml <key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict>即允许该扩展发起任意明文 HTTP 请求,绕过 ATS。
风险解读:这是"最小权限面"上的不一致——一个职责仅为"通知服务扩展"的组件,却拥有全 App 最宽松的网络策略。通知服务扩展运行在推送到达的后台时机,若其拉取图片/富媒体内容的 URL 可控或校验不足,则明文 HTTP 通道可被中间人(同 Wi-Fi 攻击者、恶意代理)篡改/注入。Apple 审核亦会要求 NSAllowsArbitraryLoads 提供合理说明。建议:仅对确需的白名单域名使用 NSExceptionDomains,或在扩展内强制 HTTPS,移除全局任意加载。
5. 能力(Capabilities / Entitlements)#
| 能力 | Entitlement | 值 | 说明 |
|---|---|---|---|
| Game Center | com.apple.developer.game-center |
true |
接入苹果游戏中心(排行榜/成就)。 |
| Sign in with Apple | com.apple.developer.applesignin |
[Default] |
支持"通过 Apple 登录",属账号体系。 |
| 关联域名 | com.apple.developer.associated-domains |
applinks:appleunilink.campfiregames.cn、applinks:xfmr.adj.st |
Universal Link:前者疑为苹果生态统一链接/自有域名,后者 xfmr.adj.st 为 Adjust 短链域名,用于归因跳转。 |
| 推送环境 | aps-environment |
production |
生产 APNs,配合 remote-notification 后台模式与通知服务扩展。 |
| 团队标识 | com.apple.developer.team-identifier |
HM5Y58WVBP |
签名团队。 |
未发现 iCloud/KVStore、HealthKit、Wallet、App Groups 等敏感权益;未发现 group.* App Group(主 App 与 Appex 之间无共享容器声明)。
6. 广告 / 归因标识#
6.1 CenturyGameResources.bundle/mopub-sdk-id.json —— SKAdNetwork 标识列表
- 该文件为 JSON 数组,共 154 条唯一
SKAdNetworkIdentifier(无重复),字段格式{"SKAdNetworkIdentifier":"<id>.skadnetwork"}。 - 前若干条示例:
22mmun2rn5.skadnetwork, 238da6jt44.skadnetwork, 24t9a8vw3c.skadnetwork, 252b5q8x7y.skadnetwork, 275upjj5gd.skadnetwork, ...末尾:... x5l83yy675.skadnetwork, 7fmhfwg9en.skadnetwork, qu637u8glc.skadnetwork - 经验证的常见广告网络条目存在:
cstr6suwn9.skadnetwork、f38h382jlk.skadnetwork、v9wttpbfk9.skadnetwork、n38lu8286q.skadnetwork、mtkv5xtk9e.skadnetwork、5a6flpkh64.skadnetwork、22mmun2rn5.skadnetwork均为True。 - ⚠️ 重要不一致:主 App 的
Info.plist不含SKAdNetworkItems键(PlistBuddy Print :SKAdNetworkItems→Does Not Exist)。SKAdNetwork 标识仅存在于资源 JSON 中。结合CenturyGameResources.bundle/merge_infoplist.py的逻辑(仅当gad_appid非空时才把mopub_data写入pl['SKAdNetworkItems']),可推断本 CN 构建未把该列表合并进运行时 Info.plist——即 SKAdNetwork 归因在系统层面很可能未生效(或依赖其他注入途径)。这属于配置/构建缺陷,需人工在目标环境复核。 NSAdvertisingAttributionReportEndpoint指向https://adjust-skadnetwork.com(但末尾带 U+200B,见 §2.1)。
6.2 ddCommon.json(根目录,明文 JSON,含大量密钥/标识)
| 字段 | 值(逐字) | 说明 |
|---|---|---|
platform.ios.wechat_app_id |
wx32374c9038cf9759 |
微信开放平台 AppID(与 CFBundleURLTypes 一致)。 |
platform.ios.douyin_client_key |
awebivvetg90lv93 |
抖音开放平台 client_key(与 URL Scheme 一致)。 |
platform.ios.bugly_id |
341c16d88c |
腾讯 Bugly/CrashSight iOS App ID。 |
platform.android.bugly_id |
1ea45351f9 |
Android 侧 Bugly ID(同发布仓)。 |
platform.*.game_id |
20173 |
游戏 ID(与 URL Scheme ddplatform-20173、com.diandian.logger.20173 一致)。 |
platform.android.access_id / access_key |
1580019064 / AQC2K08T7CLS |
腾讯 TPNS 推送 access_id/key(明文)。 |
platform.android.XG_SERVER_SUFFIX |
tpns.sh.tencent.com |
腾讯移动推送服务端。 |
id |
3281 |
应用/配置 ID。 |
helpshift / helpshift_ios |
8c855fd7c4cc6b899e2c64f37ecc65b3 |
Helpshift 客服 SDK ID。 |
adjust |
c1540cc8f7...(长十六进制串) |
Adjust 归因配置(已编码/加密)。 |
constants / secret / tga / rum.* / smarthelp.* / umbra_gateway.* |
多段长十六进制密文 | 服务端配置与密钥材料(生产/沙箱两套)。 |
风险:ddCommon.json、UOPSDKConfig.json 以明文形式随包分发(位于 .app 根),其中包含 access_key、客服 ID、部分凭据密文等;虽大部分为客户端可见配置,但结合 tga_encryption_info、secret* 等字段,值得作为"客户端硬编码密钥"专项审计对象。
6.3 UOPSDKConfig.json
{
"app_id": "789841",
"osdk_type": "Omnichannel_ActivityLink",
"client_key": { "douyin_key": "" },
"debug_mode": false
}
app_id = 789841为字节开放平台应用 ID;douyin_key在 CN 构建中为空;debug_mode = false。
7. 隐私画像总结与风险评级#
7.1 数据采集面
本 App 的第三方 SDK 组合呈现"游戏引擎 + 双归因(Adjust / 字节 UOP)+ 双分析(ThinkingData / 字节 AppLog)+ 崩溃(腾讯 CrashSight)+ 推送(Firebase Installation + 腾讯 TPNS)"的典型重度商业化矩阵:
- 追踪类(Tracking=true):
AdjustSdk(DeviceID → 第三方广告)、UnionOpenPlatformCore(DeviceID / 诊断数据,追踪域名*-tracking.volceapplog.com)。 - 分析类:ThinkingData(含粗略位置、ProductInteraction、UserID、DeviceID)、Unity 自研 Report(PerformanceData / OtherUsageData)。
- 崩溃/性能类:CrashSight(CrashData / PerformanceData / DeviceID / UserID)、UOP 内嵌 RangersAPM / HMDEmbed。
- 标识符:Adjust(含签名
ADJSigner)、字节 BDA Signal、Firebase Installation ID、微信 AppID、抖音 client_key、Bugly ID。
7.2 风险清单(按严重度)
| 级别 | 发现 | 依据 |
|---|---|---|
| 🔴 高 | 23 个框架中,聚合隐私清单与主 App 自述矛盾:主 App NSPrivacyTracking=false,但 AdjustSdk 与 UnionOpenPlatformCore 均声明 NSPrivacyTracking=true 且采集 DeviceID 用于追踪/第三方广告,并列出追踪域名。若未在 App Store Connect 正确申报"用于追踪",存在合规与审核风险。 |
各 PrivacyInfo.xcprivacy |
| 🔴 高 | 推送扩展开启 NSAllowsArbitraryLoads=true,而主 App 未开启,网络策略不一致,扩展可在后台发起明文 HTTP。 |
Appex Info.plist(见 §4) |
| 🟠 中 | SKAdNetwork 列表(154 条)未合并进主 App Info.plist,归因在系统层面可能失效;同时 NSAdvertisingAttributionReportEndpoint 末尾含零宽空格(U+200B),疑似非法 URL。 |
mopub-sdk-id.json、Info.plist、merge_infoplist.py |
| 🟠 中 | NSLocationWhenInUseUsageDescription = "empty",声明能力但文案未填,属合规瑕疵;框架(ThinkingData)声明采集 CoarseLocation。 |
主 App Info.plist |
| 🟡 低 | 明文敏感配置随包分发:ddCommon.json / UOPSDKConfig.json 含 access_key、bugly_id、secret*、tga_encryption_info 等(多为客户端配置,但应做密钥分离)。 |
根目录 JSON |
| 🟡 低 | bytedanceAttribution 将主 Bundle ID com.gof.china 注册为 URL Scheme,可能被其他 App 探测/唤起,且存在一个空 CFBundleURLTypes 条目。 |
主 App Info.plist |
| 🟡 低 | 广泛的第三方 App Scheme 探测(FB/WhatsApp/微信/抖音/微博),跨 App 拉活能力显著。 | LSApplicationQueriesSchemes |
| ⚪ 信息 | 未发现 App Group / iCloud / HealthKit / Wallet 等高敏感权益;Appex 权限极简(仅 application-identifier)。 |
codesign entitlements |
7.3 建议
- 统一网络策略:移除 Appex 的
NSAllowsArbitraryLoads,改用NSExceptionDomains白名单或强制 HTTPS。 - 修正
NSAdvertisingAttributionReportEndpoint的 U+200B,并确认SKAdNetworkItems是否正确注入到发布版Info.plist。 - 在 App Store Connect 按实际 SDK 行为申报"追踪"数据,修正主 App 与框架隐私清单的语义差异;补全
NSLocationWhenInUseUsageDescription文案或移除该键。 - 抽查 Adjust / UOP 归因回传报文的 HTTPS 与签名校验;对
ddCommon.json中疑似密钥字段做安全审计。 - 以
UnionOpenPlatformCore(体积最大、追踪域名最多)与UnityFramework(攻击面最大)为优先逆向目标,核查其网络与 JSBridge 边界。
附:本报告所有标识符与版本号均来自产物文件的直接读取(plutil / PlistBuddy / codesign / strings / python3)。凡产物中无法确证的项(如 CrashSight 内部小版本、SKAdNetwork 标识对应的广告网络归属)均未臆断;相关结论以"可能/疑似"标注。
03 · Unity 资源与内容保护分析(Whiteout Survival CN / 无尽冬日)#
目标:extracted/Payload/whiteoutsurvivalcn.app(已脱壳/已解密 IPA,Bundle 版本 1.15.6 / Build 135,Bundle ID com.gof.china)。
引擎:Unity 2022.3.52f1,脚本后端:IL2CPP(arm64 Mach-O),热更:IFix + ToLua。
本文所有结论均基于对原始文件的二进制解析与实验,未修改 app 包体(临时文件写在 artifacts/)。
1. Data/ 目录布局总览#
whiteoutsurvivalcn.app/
├── whiteoutsurvivalcn # 138,720 B Mach-O arm64 启动器
├── Frameworks/UnityFramework.framework/UnityFramework # IL2CPP 主体二进制
├── Data/
│ ├── boot.config # build-guid=2a9abc3bc9814e5498a345cecaa532da
│ ├── globalgamemanagers # 341,444 B(明文标准序列化文件头)
│ ├── globalgamemanagers.assets # 686,836 B
│ ├── resources.assets # 2,340,004 B(明文)
│ ├── level0 / level1 / level2 # 场景(明文)+ *.resS 流数据
│ ├── sharedassets0/1/2.assets
│ ├── unity default resources # 695,864 B
│ ├── ScriptingAssemblies.json # 125 个程序集清单
│ ├── RuntimeInitializeOnLoads.json
│ ├── Managed/
│ │ ├── Metadata/global-metadata.dat # 9,902,340 B(加密,见 §2)
│ │ ├── Resources/mscorlib.dll-resources.dat
│ │ └── mono/4.0/machine.config # 仅 33,598 B(无托管 DLL)
│ ├── Resources/unity_builtin_extra # 425,220 B
│ └── Raw/
│ ├── data/ # 5,316 个 .unity3d AssetBundle,合计 715 MB
│ ├── world/ # 4 个 .bytes 自定义二进制配置(非 UnityFS,见 §5)
│ ├── sound/ # 36 个 CriWare .acb/.awb/.acf(39 MB,见 §7)
│ └── game_res_mark # 3,546 B(本身也是一个 UnityFS Bundle)
Data/Managed/下没有任何托管 DLL,只有Metadata/global-metadata.dat与mono/4.0/machine.config→ 确认为 IL2CPP 全 AOT 编译;业务 C# 代码已编入Frameworks/UnityFramework.framework/UnityFramework(Mach-O arm64)。Data/Raw/*是随包分发的 StreamingAssets 预置资源(首发资源),其余通过AssetDataInfo.GetUrl从 CDN 增量下载。
Unity 版本证据
$ xxd -l 48 Data/globalgamemanagers
00000000: 0000 0000 0000 0000 0000 0016 0000 0000
00000010: 0000 0000 0000 045c 0000 0000 0005 35c4
00000020: 0000 0000 0000 0490 0000 0000 0000 0000
00000030: 3230 3232 2e33 2e35 3266 3100 0900 0000 2022.3.52f1.....
$ strings -a Data/globalgamemanagers | grep -E "^20[0-9][0-9]\.[0-9]"
2022.3.52f1
globalgamemanagers、resources.assets、level0、sharedassets*、unity_builtin_extra 全部为明文标准 Unity 序列化文件(文件头 <metadataSize><fileSize><version=0x16=22>... + 版本串 2022.3.52f1),可被 AssetStudio / UnityPy 直接读取、未加密。
ScriptingAssemblies.json(共 125 项)
前 63 项为 UnityEngine.* 模块;其余 62 项为游戏/第三方程序集,关键项:
| 类别 | 程序集 |
|---|---|
| 游戏框架 | Assembly-CSharp.dll, Assembly-CSharp-firstpass.dll |
| 自研框架 | CenturyGame.GameFramework.dll, CenturyGame.ShaderFramework.ShaderLibrary.dll, CenturyGame.ShaderLibrary.Builtin.dll |
| 热更(IFix) | IFix.Core.dll, Debugger.dll |
| Lua 生态 | (Lua 虚拟机在原生层,*.Wrap 绑定见 metadata) |
| CriWare 音频 | CriMw.CriWare.Runtime.dll |
| 数数/抖音 SDK | Unity.dd.sdk.scripts.core/account/accountcn/payment/rate/push/adjust/wechat/tga/bugly/reyun/wjx/smartHelp/tools/advertising/report/helpshift.dll(+ 各 .demo.dll) |
| 其它 | spine-unity.dll, OdinSerializer.dll, com.Tivadar.Best.HTTP.dll, Unity.AI.Navigation.dll, Unity.Notifications.Unified/iOS.dll, HdgRemoteDebugRuntime.dll(远程调试!), CString.dll, PathCreator.dll, Unity.ScriptableBuildPipeline.dll |
RuntimeInitializeOnLoads.json
SRDebugger.AutoInitialize.OnLoadBeforeScene / OnLoad (Assembly-CSharp)
com.Tivadar.Best.HTTP $BurstDirectCallInitializer
OdinSerializer ...
Unity.MemoryProfiler.MetadataInjector
即游戏内自带 SRDebugger 调试面板 与 HdgRemoteDebugRuntime 远程调试运行时,逆向时可用作入口线索。
2. global-metadata.dat 加密(IL2CPP 元数据保护)#
- 文件大小 9,902,340 B,
Data/Managed/Metadata/下打包态即为密文;密文头为E1 4F F4 A9(明文魔数应为AF 1B B1 FA= IL2CPP Metadata v31)。 - 加密方案(由本项目
artifacts/decrypt_global_metadata.py验证):整文件 17 字节循环 XOR,密钥
KEY = b"NTESCSLLVMPASSKEY" # 17 字节("NTESCSLLVMPASSKEY")
解密后魔数 0xFAB11BAF 校验通过,version = 31,字符串表完全可读 → 已成功还原出
out_identifiers.txt(2.1 MB) / out_literals.txt(567 KB) / out_types.txt(278 KB)。
- 采用 17 字节周期使密文呈现约 73% 可打印字符,属于伪装式周期 XOR,并非强加密;但由于游戏另有 Enable_Encrypt_NHP(网易易盾 NetEase 安全通信) 与 IFix 热更,说明官方安全重心在运行期而非静态资源。
3. AssetBundle 容器格式:明文 UnityFS(关键发现)#
对 Data/Raw/data 中全部 5,316 个 .unity3d 扫描:
$ cd Data/Raw/data && grep -l "UnityFS" *.unity3d | wc -l
5316 # 5316/5316 全部以明文 "UnityFS" 开头
容器头是明文且完全合法,例如 config_login_logo_01381e8462c1673211ae1bda35252d09.unity3d:
00000000: 556e 6974 7946 5300 0000 0008 352e 782e UnityFS.....5.x.
00000010: 7800 3230 3232 2e33 2e35 3266 3100 0000 x.2022.3.52f1...
00000020: 0000 0000 05f6 0000 0041 0000 005b 0000 .........A...[..
00000030: 0243 0000 0000 0000 0000 0000 0000 0000 .C..............
00000040: 1e00 0100 b201 0000 0ec8 0000 0566 0003 .............f..
00000050: 0e00 0801 0000 1a00 f018 0004 4341 422d ............CAB-
00000060: 3566 6561 3936 3935 3663 3437 6534 3766 5fea96956c47e47f
00000070: 6235 3333 3561 3435 6530 3531 3437 3462 b5335a45e051474b
00000080: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000090: 1600 0100 1616 0b00 220d 0d08 0023 0ec8 ........"....#..
字段解析(大端):signature="UnityFS",fileVersion=8,unityVersion="5.x.x",unityRevision="2022.3.52f1",size=0x5f6=1526,blocksInfoCompressed=0x41=65,blocksInfoUncompressed=0x5b=91,flags=0x0243。
flags & 0x3F = 3→ 块压缩算法 LZ4HC;flags & 0x200→BlockInfoNeedPaddingAtStart(2022.1.1+ 语义),因此 header 之后按 16 字节对齐到0x40才是 blocksInfo;- blocksInfo 可正常解压且为明文,完整读出目录信息(CAB 名、节点偏移/大小)。以自研解析器(
artifacts/parse2.py)验证:
FILE config_login_logo_...unity3d (1526B)
header: ver=8 engine=5.x.x player=2022.3.52f1 size=1526 ci=65 ui=91 flags=0x00000243
blocksInfo decompressed OK: 91B (declared 91)
uncompressedDataHash=00000000000000000000000000000000 blocks=1
block[0] uncompressed=3784 compressed=1382 flags=0x0003
nodes=1
node[0] offset=0 size=3784 flags=0x00000004 path='CAB-5fea96956c47e47fb5335a45e051474b'
注意:uncompressedDataHash 恒为 全 0(正常 Unity 应为内容哈希),疑似打包工具抹除。
3.1 加密位置:仅首个压缩块被加密
关键证据:UnityPy 1.25.2 打开上述包时抛异常,但其 traceback 精确指向 block data 解压(BundleFile.py:174-175 → CompressionHelper.decompress_lz4),而非 blocksInfo 解压(BundleFile.py:141):
File "UnityPy/files/BundleFile.py", line 49, in __init__
m_DirectoryInfo, blocksReader = self.read_fs(reader)
File "UnityPy/files/BundleFile.py", line 174, in read_fs
b"".join(
File "UnityPy/files/BundleFile.py", line 175, in <genexpr>
self.decompress_data(
File "UnityPy/helpers/CompressionHelper.py", line 94, in decompress_lz4
return lz4.block.decompress(data, uncompressed_size)
_block.LZ4BlockError: Decompression failed: corrupt input or insufficient space in destination buffer.
对 Data/Raw/data 随机 300 个包逐块尝试标准 LZ4HC 解压(artifacts/batch.py):
total analyzed: 300
bundles where ALL blocks decompress : 0
bundles where NO block decompresses : 145 # 单块包 → 整包无法解
bundles where block[0] fails : 300 <a id="num"></a>
# 100% 命中
per block-index [ok,fail] (first 8):
block[0]: ok=0 fail=300
block[1]: ok=155 fail=0
block[2]: ok=74 fail=0
block[3]: ok=46 fail=1 (唯一 1 例抖动)
block[4..7]: 全部 ok
结论明确:
- 容器 + blocksInfo(含目录/CAB 名)是明文可读的;
- 每个 Bundle 的第一个数据块(block[0])被加密/加扰,第 2 块起为标准 LZ4HC 明文;
- 单块包(145/300)因此整体不能被 UnityPy/AssetStudio 解析 —— 这正是"反提取"目的:block[0] 通常承载 Unity 序列化文件的文件头 + 类型树 + 对象表,破坏它即可让整包无法解析。
对 GPU 士兵包的直接证据(artifacts/parse2.py):
block[0] uncompressed=131072 compressed=80594 flags=0x0003 -> DECRYPT/COMPRESS FAIL
block[1] uncompressed=14456 compressed=13563 flags=0x0003 -> LZ4 OK -> 14456B
3.2 加密方案推断(与 metadata 符号吻合)
解密后的 IL2CPP 元数据包含如下自研符号,与上述现象一一对应:
CenturyGame.GameFramework.Helper-CalcEncryptKeyFromString0
CenturyGame.GameFramework.Helper-EncryptXOR0 / EncryptXOR1 / EncryptXOR2
CenturyGame.GameFramework.Helper-CheckLuaBytecodeHeader0
CenturyGame.GameFramework.Helper-EncryptLuaBytecode0 / DecryptLuaBytecode0
CenturyGame.GameFramework.AssetDataInfo-get_BundleEncryptOffset0
CenturyGame.GameFramework.AssetDataInfo-CalcOffsetFromString0
CenturyGame.GameFramework.AssetDataInfo-CheckFlag0 / CheckFlags0 / HasMark0 / GetUrl0
类型: AssetDataInfo / AssetBundleMgr / AssetLoader / BundleAssetLoader / BundleLoader
AssetBundleContentData / AssetBundleData / AssetBundleCache / AssetBundleDownloadHandler
推断的保护设计:
AssetDataInfo.BundleEncryptOffset+CalcOffsetFromString(...):由资源名/URL 字符串派生一个加密偏移,用于定位加密区间的起点/长度;Helper.CalcEncryptKeyFromString(...)+EncryptXOR/1/2:由字符串派生 XOR 密钥,对 Bundle 的首块做异或加扰(三种变体可能对应不同资源分组/版本);- 因此这是自研 XOR 块级加密,而非 Unity 官方的 Unity CN 加密(后者 flags 应为
0x400/0x1400并带 16+16+16+16 字节的 AES-ECB 派生密钥头;本包 flags 仅0x0243,不含该位,UnityPy 亦未走ArchiveStorageDecryptor分支)。
密钥破解尝试(未成功,作为证据):
| 尝试 | 结果 |
|---|---|
| 单字节 XOR 全暴力(0x00–0xFF,12 个包) | 全部 NO |
| 2 字节循环 XOR 暴力 | 未命中(耗时过高未跑完) |
复用元数据密钥 NTESCSLLVMPASSKEY |
未命中 |
复用 CAB-<hash> / 文件名 / IFix / CenturyGame 等固定串 |
未命中 |
→ 表明密钥按资源串逐包派生(CalcEncryptKeyFromString),并非全局固定密钥,强度高于元数据的周期 XOR。
旁证(决定"是首块还是定长前缀"):在 300 采样中,只要包有 ≥2 块,block[1] 永不解压失败,且多块包中 block[0] 的压缩块最小为 14,573 B(art_effect_ui_material_common_depend_...)。这说明被加密区间与块边界对齐(即恰好第一个数据块),而不是"固定前 N 字节"式的连续前缀。
4. 内容清单(Data/Raw/data,5,316 个 .unity3d,715 MB)#
4.1 目录分类(按文件名首两段统计)
| 分类 | 数量 | 说明 |
|---|---|---|
ui_panel / ui_panelitem / ui_single |
2,403 + 82 + 20 | UI 面板 Prefab/贴图 |
scene_battle |
687 | 战斗场景、特效、GPU 士兵 |
scene_city |
559 | 城市/建筑/地表动画 |
art_effect |
326 | 通用特效 |
art_actor |
245(另 art_actorrig 28) |
角色骨骼/3D |
actor_battle |
133 | 战斗角色(英雄红/蓝方、士兵) |
art_spine / spine_ui / spine_build |
126 + 125 + 6 | Spine 2D 立绘/动画 |
atlas_ui / atlas_fx / atlas_world |
88 + 78 + 12 | 图集 |
scene_world |
68 | 大地图(SLG) |
scene_refinescience / scene_towerdefence / scene_brawlwar / scene_starvip / scene_soldier |
55 / 38 / 6 / 9 / 7 | 各玩法场景 |
actor_hero / scene_hero |
22 + 5 | 英雄展示 |
config_* |
14 | 配置映射(config_atlas_mapping、config_sound_mapping 等) |
mat_*、shader、3party_spine_runtime、scenes_depend |
少量 | 材质/着色器/依赖 |
luapackage_32/64、toluaout_32/64、unity_mono_scripts |
5 | Lua 业务逻辑与脚本入口(见 4.3) |
4.2 代表性文件名(30 个)
actor_animation2d_spritemesh_c126e0fbcea5a30f011a656c1665e773.unity3d
actor_battle_battle_hero_eleonora_1155d29d82b0406c0625f9ba3d610f60.unity3d
actor_battle_battle_hero_judge_red_7447a4cf3744a4a21effe75c5dc0c7c6.unity3d
actor_battle_battle_hero_smith_a07b5a03fa060d11124feb28bc2c2c92.unity3d
actor_battle_battle_gpu_soldier_archer_blue_59ffd7348be8d3ebba087fdabed91a1e.unity3d
art_actor_hero_3d_hero_builder_anim_battle_depend_7fc535c5b4879c6c971473d337c9a61d.unity3d
art_actor_hero_3d_hero_exhibit_dominic_depend_b8d8eb14a94fb164f843b5ee478446b9.unity3d
art_actor_hero_3d_hero_exhibit_wu_ming_depend_605881138cd32d24ad58875a2817215e.unity3d
art_scene_city_build_building_pier_3d_city_anim_building_pier_01_anim_depend_0aead504dd44c057a955cc5b3ccd1959.unity3d
atlas_ui_common_94c4c36d820b45c2f28ae821bcf160ba.unity3d
atlas_ui_login_846c36fdcdbce8aa97db8d45552abdf8.unity3d
config_atlas_mapping_b583fdbab488a1bb0176a8313d68841f.unity3d
scene_battle_effect_beastmaster_fx_hero_beastmaster_attack_hit_ec6357f4237d0bd48167c3425dcd9a2f.unity3d
scene_battle_effect_captain_fx_hero_captain_skillxp_xuli_550c5fa82e69c4f07f0201128aaf7bec.unity3d
scene_battle_effect_gisela_fx_hero_gisela_xp_smoke_v_r_fml_4bb6fc594479503db3d34878adf0f637.unity3d
scene_battle_effect_hector_fx_hero_hector_skill_xp_lod_870ab33a2eafb0a7ae067405b8f6bf66.unity3d
scene_battle_effect_ligeia_fx_hero_ligeia_xpskill_boom_yly_b97d6eac9f0e551c89bee7049ce5e02d.unity3d
scene_battle_effect_molly_fx_hero_molly_skill01_hit_b85d1a4489364a3580e132b98cb65a1f.unity3d
scene_battle_effect_seo_yoon_fx_hero_yoon_xp_buff_lod_27b4cbad785323920cfc644edfaaae74.unity3d
scene_city_building_accessory_city_building_upgradeboard_05_d7fb61199c55186112e8103d4b9c8e1a.unity3d
scene_city_building_city_anim_building_fishing_ice_cave_01_4a7b46389f44c9f30fc310bc40722fda.unity3d
scene_city_building_city_building_furnace_02_50c336f062e3282411d3845d24898564.unity3d
spine_ui_puzzlesystem_cardenvelope01_anim_49db00cbd100bd2e4cfc6fc2840364ee.unity3d
ui_panel_capitalwar_capitalwarpersonalinfopanel_c6adc61f8bb7cdfc298d3dd282d45fc1.unity3d
ui_panel_hero_effect_fxui_recruit_chouka_front_purple_jxm_f693b4b130eee6de56a866cc8135481d.unity3d
ui_panel_worldscene_worldmapnestnamepanel_6ff410282a7ccf7c6fb6184928575982.unity3d
ui_panel_soldier_fxui_soldie_pikeman12_jianying_wwj_54b9b4cdb86460ea26088cd66ba22456.unity3d
ui_panel_dress_dresspreviewpanel_d2bb496996770a4afd952c4e6e5400d0.unity3d
luapackage_32_974121449b64808d202866a5fe28f953.unity3d
4.3 Lua / 业务逻辑 Bundle(重要)
包名中含 lua / tolua 的共 9 个(真正承载逻辑的 5 个):
luapackage_32_974121449b64808d202866a5fe28f953.unity3d (20,161,980 B)
luapackage_64_743799ffbb0b96aabfb80d262995bb74.unity3d (20,194,704 B)
toluaout_32_760a415d51765d7d69bbfa7db9e1af14.unity3d
toluaout_64_c52eccf42487f8303f69a2d41c5aba16.unity3d
unity_mono_scripts_8b2eb1dcfa8c5ba7bd0c39f4d6257e85.unity3d
- 采用 ToLua(
luapackage+toluaout+ metadata 中大量*Wrap绑定,如UnityEngine_AssetBundleWrap、CriWare_CriAtomListenerWrap)→ 大部分界面/玩法业务逻辑用 Lua 编写,以 TextAsset 形式打进 AssetBundle。 luapackage_32结构(artifacts/parse2.py):blocks=406,每块uncompressed=131072(128 KB),path='CAB-c6d925562e3180c1714285c203e48d85'。- 由于仅 block[0] 被加密,我们跳过首块、解出其余 405 块(53,046,432 B),可见大量 Lua 视图/逻辑字符串:
$ strings -a luapackage_32_*.partial.bin | grep -icE "function|local |return|require|tolua"
5310
$ strings -a luapackage_32_*.partial.bin | head
-DumpSaveData: value is function, path=%s
game_module_ui_view_findinggame_FindingGameReturnEnergyTipView
TipViewBaseNew GUIBase#FindingGameReturnEnergyTipView
...
→ 证实 游戏业务逻辑确实在加密 Lua Bundle 内。此外 Helper.CheckLuaBytecodeHeader / EncryptLuaBytecode / DecryptLuaBytecode 表明 Lua 字节码/源码额外做了一层加密(luapackage 首块被 XOR 保护,字节码本身可能再被 EncryptLuaBytecode 处理)。
5. Data/Raw/world/*.bytes(自定义二进制,非 AssetBundle)#
4 个文件均不是 UnityFS:
map_world_config_item_72ad90ca83f877f34cc2e16af679c906.bytes 32,942 B
map_world_config_mask_6d8d1979ff02460f3df6faf566a7a01c.bytes 720,004 B
map_world_config_setting_29d541c1d26eff676e2d02b24932a7d7.bytes 248 B
map_world_config_terrain_5199904f17161e093854c3716634bcd2.bytes 526,137 B
setting 头部 hexdump:
00000000: 0100 0000 b004 0000 0000 4040 0000 a043 ..........@@...C
00000010: 0000 803f 0000 8c42 cdcc 4c3f 6666 a63f ...?...B..L?ff.?
00000020: 2ba7 0540 a283 2140 a283 e13f 6795 1340 +..@..!@...?g..@
item 头部:0100 0000 7600 0000 0356 0000 0d56 0000 ...(0x01 版本 + 计数 + 递增索引)。
- 这些是世界地图(SLG 大世界)配置,采用自研紧凑二进制 + IEEE-754 小端浮点;可能经
EncryptXOR变体处理(首字节版本标记0x01保留,其余为数值)。与 AssetBundle 是两套不同的存储/加密路径(Data/Raw/worldvsData/Raw/data)。
6. 音频:CriWare(CRI ADX2 / AFS2)#
Data/Raw/sound/ 共 36 个文件,39 MB,全部为 CriWare(CRIWARE)中间件 容器:
$ xxd -l 16 acf_GOF_CRI_...acf # ACF 全局配置
00000000: 4055 5446 0000 3e58 0001 0158 0000 029a @UTF..>X...X....
$ xxd -l 16 acb_sound_login_...acb # ACB
00000000: 4055 5446 0009 2d58 0001 01f8 0000 03af @UTF..-X........
$ xxd -l 16 awb_music_guide_...awb # AWB
00000000: 4146 5332 0204 0200 0500 0000 2000 0000 AFS2........ ...
@UTF= CriWare ACB UTF 表(ACB Format/PC Ver.1.37.1 Build:);AFS2= AWB 音频流容器。- 元数据确认存在
CriWare.CriFsLoadAssetBundleRequest、CriWare_CriAtomListenerWrap、CriWare_CriMonoBehaviourWrap,以及CriMw.CriWare.Runtime.dll→ 使用 CRI Atom / ADX2,音频自带 CRI 加密密钥体系(ACB Header 中的Crypt字段 + AWB Key;解密需CriWareDecrypter/acb2wav+ 对应 key)。 - 语音/节日资源可见:
acb_zh_voice_story_local_*、acb_zh_voice_heroexhibit_local_*、acb_music_loading_holiday_dragonboat2026_*、acb_music_loading_holiday_midautumn2026_*、acb_music_loading_holiday_worldcup2026_*、acb_sound_battle_*、acb_sound_slg_*等。
注意:CriWare 的加密是独立于 Unity 资源加密的第二套保护,即使解密了 Bundle,音频仍需 CRI key 才能还原。
7. 结论与保护强度评估#
| 层面 | 保护方式 | 实际强度 |
|---|---|---|
| C# 逻辑 | IL2CPP AOT + global-metadata.dat 17 字节循环 XOR |
弱(周期 XOR 易解,已还原全部符号) |
| 程序集 | 无托管 DLL 随包;IFix 热更 | 中(需动态抓取 IFix patch) |
| AssetBundle | 明文 UnityFS + 仅首块自研 XOR 加密(CalcEncryptKeyFromString + BundleEncryptOffset) |
中:容器/目录明文,只要破解首块密钥即可 100% 还原资源 |
| Lua 业务逻辑 | ToLua luapackage/toluaout + 首块 XOR + EncryptLuaBytecode |
中:逻辑集中在这 5 个包,是逆向重点 |
| 世界地图配置 | 自研二进制(Data/Raw/world/*.bytes) |
中(需逆格式) |
| 音频 | CriWare ADX2 + CRI key | 中(独立密钥) |
| 运行期 | 网易易盾 Enable_Encrypt_NHP / NetEase_* 安全通信、SRDebugger、HdgRemoteDebugRuntime |
运行时网络加密,静态无关 |
逆向利用路径(建议):
1. 元数据已解 → 从 out_identifiers.txt 定位 Helper.EncryptXOR0/1/2、CalcEncryptKeyFromString、AssetDataInfo.CalcOffsetFromString,在 UnityFramework 中定位实现以还原首块密钥算法;
2. 解出首块后,把每个 .unity3d 的 block[0] 还原为标准 LZ4HC 流,即可用 UnityPy/AssetStudio 全量导出贴图、Spine、Prefab、Shader;
3. 针对 luapackage_32/64,反编译 Lua 字节码(注意 DecryptLuaBytecode/CheckLuaBytecodeHeader 的二次加密)以获取业务逻辑;
4. 音频使用 CriWare 对应 key 解密 ACB/AWB。
附:本次分析复现脚本(均位于 artifacts/)
| 脚本 | 用途 |
|---|---|
decrypt_global_metadata.py |
还原 global-metadata.dat(17 字节 XOR) |
parse_unityfs.py / parse2.py |
UnityFS 头 + blocksInfo + 逐块解压解析 |
unitypy_test.py |
UnityPy 加载复现失败(失败证据) |
batch.py |
300 包逐块成功率统计(block[0] 全失败) |
crack.py / keytest.py |
单/双字节 XOR 与候选密钥爆破(均未命中) |
dump_blocks.py |
跳过首块、导出其余块内容(Lua 逻辑取证) |
04 - 二进制结构、运行时保护与反篡改分析#
目标:Whiteout Survival CN / 无尽冬日(com.gof.china,版本 1.15.6),已解密 IPA(com.gof.china_1.15.6_und3fined.ipa)。
App 包路径:extracted/Payload/whiteoutsurvivalcn.app
分析日期:2026-10-12 | 平台:macOS (darwin),工具链:file / otool / lipo / nm / strings / plutil
本次分析仅做只读检查,未修改 App 包内任何文件。
0. 二进制清单与总体结构#
0.1 三主二进制概况
| 二进制 | 相对路径 | 大小 | file 类型 | CPU | 文件类型 |
|---|---|---|---:|---|---|---|
| 主可执行文件 | whiteoutsurvivalcn | 138,720 B | Mach-O 64-bit executable | arm64 | MH_EXECUTE |
| Unity 引擎库 | Frameworks/UnityFramework.framework/UnityFramework | 77,587,120 B | Mach-O 64-bit dylib | arm64 | MH_DYLIB |
| 推送扩展 | PlugIns/CGPushServerExtension.appex/CGPushServerExtension | 74 KB 级 | Mach-O 64-bit executable | arm64 | MH_EXECUTE |
lipo -info 对三者均返回 Non-fat file ... architecture: arm64,即均为单架构 arm64(无 arm64e / 无 32 位 slice)。主可执行文件是一个"瘦启动器"(thin launcher,全文仅约 12 条可打印字符串),真正的游戏逻辑全部位于 UnityFramework(IL2CPP)与其元数据中。
0.2 关键 Mach-O 头信息(otool -h / otool -hv)
| 二进制 | magic | cputype | ncmds | sizeofcmds | flags(hex) | 关键 flag 解析 |
|---|---|---|---|---|---|---|
whiteoutsurvivalcn |
0xfeedfacf |
16777228 (ARM64) | 30 | 2872 | 0x00200085 |
NOUNDEFS DYLDLINK TWOLEVEL **PIE** |
UnityFramework |
0xfeedfacf |
16777228 (ARM64) | 93 | 10880 | 0x00910085 |
NOUNDEFS DYLDLINK TWOLEVEL BINDS_TO_WEAK NO_REEXPORTED_DYLIBS MH_HAS_TLV_DESCRIPTORS(dylib,PIE 由加载器保证) |
CGPushServerExtension |
0xfeedfacf |
16777228 (ARM64) | 31 | 3592 | 0x00200085 |
NOUNDEFS DYLDLINK TWOLEVEL **PIE** |
结论:
- 两个可执行文件均带 MH_PIE(ASLR 已启用,0x200000 位)。
- UnityFramework 是 MH_DYLIB,无 MH_PIE 标志(dylib 本身在进程内由 dyld 随机化加载)。
0.3 加密状态 LC_ENCRYPTION_INFO_64(cryptid)
| 二进制 | cryptoff | cryptsize | cryptid | 含义 |
|---|---|---|---|---|
whiteoutsurvivalcn |
81920 | 16384 | 0 | 已解密(__TEXT 可读) |
UnityFramework |
802816 | 68730880 | 0 | 已解密(cryptsize ≈ 全部 __TEXT) |
CGPushServerExtension |
16384 | 16384 | 0 | 已解密 |
三个二进制的 cryptid 均为 0,确认 IPA 已完成 App Store 加壳解密(属于 "und3fined"(越狱工具)解密产物,非原始加密 IPA)。LC_BUILD_VERSION:主二进制与 UnityFramework 均为 platform 2 (iOS)、minos 12.0、sdk 26.1。
0.4 栈保护与 ARC 证据
命令:nm -u <bin> | grep -i stack_chk,nm -u <bin> | grep -E "_objc_(retain|release|autorelease|storeStrong)"
| 二进制 | ___stack_chk_fail / ___stack_chk_guard |
数量 | ARC 符号(_objc_*) |
|---|---|---|---|
whiteoutsurvivalcn |
❌ 未引用 | 0 | 部分:objc_release、objc_retainAutoreleasedReturnValue、objc_autoreleasePoolPush/Pop(Unity 启动器样板) |
UnityFramework |
✅ ___stack_chk_fail, ___stack_chk_guard |
2 | 完整:objc_retain/storeStrong/storeWeak/autorelease/retainBlock 等 12 项 |
CGPushServerExtension |
✅ ___stack_chk_fail, ___stack_chk_guard |
2 | 完整 objc_retain/release/storeStrong 等 |
结论:UnityFramework 与推送扩展开启了栈溢出保护(stack canary)并使用 ARC;主可执行文件作为极简 Unity 启动器未启用 canary(无自有 C 逻辑)。此外 UnityFramework 引用了 _ptrace、_sysctl、_sysctlbyname、_fork、_forkpty、_pthread_atfork —— 这些是反调试 / 环境探测的典型依赖(详见第 3 节)。
0.5 链接的动态库(otool -L)
主可执行文件(11 项,仅 1 个 @rpath 第三方库):
/usr/lib/libz.1.dylib
/usr/lib/libsqlite3.dylib
/usr/lib/libicucore.A.dylib
/System/Library/Frameworks/SwiftUI.framework/SwiftUI (weak)
/System/Library/Frameworks/StoreKit.framework/StoreKit
/System/Library/Frameworks/Foundation.framework/Foundation
/usr/lib/libobjc.A.dylib
/usr/lib/libc++.1.dylib
/usr/lib/libSystem.B.dylib
/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
@rpath/FirebaseNIMessInstallation.framework/FirebaseNIMessInstallation
推送扩展 CGPushServerExtension.appex(13 项):
/System/Library/Frameworks/CFNetwork.framework/CFNetwork
/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics
/System/Library/Frameworks/CoreMedia.framework/CoreMedia
/System/Library/Frameworks/CoreMotion.framework/CoreMotion
/System/Library/Frameworks/CoreTelephony.framework/CoreTelephony
/System/Library/Frameworks/Foundation.framework/Foundation
/System/Library/Frameworks/UIKit.framework/UIKit (weak)
/System/Library/Frameworks/AdSupport.framework/AdSupport
/System/Library/Frameworks/UserNotifications.framework/UserNotifications
/System/Library/Frameworks/UserNotificationsUI.framework/UserNotificationsUI
/usr/lib/libobjc.A.dylib
/usr/lib/libSystem.B.dylib
/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
LC_RPATH(主二进制):/usr/lib/swift、@executable_path/Frameworks、@loader_path/Frameworks。
LC_LOAD_DYLIB @rpath 第三方框架(全部位于 UnityFramework):AFNetworking、AdjustSdk、AdjustSigSdk、BDASignalSDK、Protobuf、ThinkingDataCore、ThinkingSDK、UnionOpenPlatformCore、UnionOpenPlatformDataLink、CrashSightPlugin、CrashSightAdapter、CrashSightCore、CrashSight、FirebaseNIMessInstallation 以及 libswift*.dylib 系列。
1. UnityFramework 链接的系统框架(otool -L 全量)与隐私/权限映射#
UnityFramework 共链接 77 个 dylib/framework(otool -L "$UF" | tail -n +2 | wc -l → 77)。以下按隐私/能力域归类(全部为系统框架,第三方见第 7 节):
| 归类 | 框架 | 隐私 / 权限含义 |
|---|---|---|
| 广告与追踪 | AdSupport(weak)、AdServices(weak)、AppTrackingTransparency(weak) |
读取 IDFA/ASIdentifier;ATT 授权弹窗(NSUserTrackingUsageDescription) |
| 设备与网络 | CoreTelephony |
读取运营商、SIM/网络信息(≈ 设备指纹) |
| 运动传感器 | CoreMotion(weak) |
加速度/陀螺仪/计步(隐私敏感) |
| 相册与媒体 | Photos、PhotosUI(weak)、AssetsLibrary、AVFoundation(weak)、AVKit、MediaPlayer、MediaToolbox、CoreMedia、CoreVideo、VideoToolbox、ImageIO |
相册/相机/麦克风/视频播放(截图分享、客服上传) |
| 支付 | StoreKit(weak) |
App 内购买 / 收据校验 |
| 登录/社会化 | AuthenticationServices、SafariServices、WebKit、Social、JavaScriptCore |
网页登录、微信/抖音/H5 授权、分享 |
| 游戏/手柄 | GameKit、GameController(weak) |
Game Center、外接手柄 |
| 安全 | Security |
Keychain / 证书 / 加密原语 |
| 系统配置 | SystemConfiguration、MobileCoreServices、CoreServices、UniformTypeIdentifiers(weak) |
网络可达性、通用类型 |
| 账户/日历 | Accounts、EventKit |
系统账户、日历(一般由统计/推送 SDK 引入) |
| 图形/计算 | Accelerate(weak)、Metal、OpenGLES、CoreGraphics、QuartzCore、CoreImage、CoreText |
渲染与信号处理 |
| 地图 | MapKit(weak) |
定位/地图展示 |
| 崩溃监控 | MetricKit(weak) |
系统级崩溃/性能指标 |
| 通知 | UserNotifications(weak) |
本地/远程通知 |
| 音频 | AudioToolbox、OpenAL |
音频引擎 |
配套 PrivacyInfo.xcprivacy:声明访问 SystemBootTime、DiskSpace、UserDefaults、FileTimestamp;收集 DeviceID(linked=true,用途 Analytics/Other)与 PhotosorVideos;NSPrivacyTracking = false(但实际链接了 AdSupport/ATT,声明与能力存在张力)。Info.plist 中 NSAdvertisingAttributionReportEndpoint = "https://adjust-skadnetwork.com"(注意字符串尾部含一个零宽空格 U+200B)。
2. 反篡改 / 反调试 / 反作弊证据(字符串 + 符号)#
说明:游戏字符串主要存在于 IL2CPP 元数据(已解密到 artifacts/out_literals.txt / out_identifiers.txt / out_types.txt)以及 UnityFramework 的 Mach-O 字符串表中。以下均给出精确命中串 + 来源文件。
2.1 越狱检测(jailbreak / Cydia / Substrate)
| 精确字符串 | 来源 | 上下文 / 含义 |
|---|---|---|
/Applications/Cydia.app |
unity.strings:58268 |
经典越狱路径探测 |
/Library/MobileSubstrate/MobileSubstrate.dylib |
unity.strings:58269 |
Substrate 注入框架探测 |
/usr/lib/libsubstrate.dylib |
unity.strings:58273 |
Substrate 库探测 |
/private/var/lib/apt/ |
unity.strings:58267 |
Cydia/APT 包管理残留 |
/etc/apt |
unity.strings:58270 |
同上 |
cydia:// |
unity.strings:58271 |
Cydia URL scheme 探测 |
/bin/bash、/usr/sbin/sshd |
unity.strings:64097-64098 |
越狱环境常见可执行文件 |
MobileSubstrate |
unity.strings:60232 |
名称匹配 |
cydiaSubstrate; / cYDIAsUBSTRATE |
unity.strings:26109 / 26123 |
变体/混淆匹配 |
isJailBreak |
unity.strings:46323 |
ObjC 方法或 IL2CPP 包装方法 isJailBreak |
jailbrokenStatus / _jailbrokenStatus / setJailbrokenStatus: |
unity.strings:46495 / 40847 / 50796 |
ObjC 属性(TQ,N,V_jailbrokenStatus,unity.strings:60255) |
/var/lib/dpkg/info/mobilesubstrate.md5sums |
fw_UnionOpenPlatformCore.strings:1773 |
字节跳动 UnionOpenPlatform SDK 内部的越狱检测 |
JailbrokenStatus 以 ObjC 属性/IL2CPP 符号形式存在(_jailbrokenStatus + setJailbrokenStatus: + 类型编码 TQ,N,V_jailbrokenStatus),说明越狱状态被读取并在 C#/ObjC 层保存。
2.2 反调试
| 精确字符串 | 来源 | 含义 |
|---|---|---|
isDebuggerAttached |
unity.strings:46262 |
调试器附加探测 |
isDebuggerAttachedToConsole |
unity.strings:57275 |
调试器/控制台探测 |
sysctl.proc_native |
unity.strings:83068 |
sysctl 环境探测 |
未定义符号 _ptrace、_sysctl、_sysctlbyname |
nm -u UnityFramework |
反调试系统调用依赖 |
SRDebugger / SRDebuggerInit / OpenSRDebugger |
out_types.txt / out_literals.txt |
内置调试面板(正式包默认关闭,但代码在) |
OpenZbsDebugger、openLuaDebugger、LuaInterface.Debugger |
out_literals.txt:14663 等 |
Lua 调试器入口(潜在攻击面) |
2.3 反作弊(核心为网易 NetEase NetSec / 易盾体系)
| 精确字符串 / 符号 | 来源 | 含义 |
|---|---|---|
NetEase.AntiCheatResult(类型) |
out_types.txt:1219 |
反作弊结果对象 |
NetEase_AntiCheatResultWrap / _CreateNetEase_AntiCheatResult |
out_identifiers.txt:11591 |
IL2CPP↔C# 桥接 |
NetEase.SafeCommResult / NetEase.NetSecProtect / NetEase.HTProtectConfig / NetEase.HTPCallback / NetEase.GetTokenCallback |
out_types.txt |
NetSec 加固/安全通信类型 |
InitNetSecProtect、NetSecGetToken、NetSecGetTokenAsync、NetSecSetConfigData、NetSecSetResponseData |
out_literals.txt:5342,6672-6675 |
反作弊初始化与令牌获取 |
NetSecGetCollectDataHelper、NetSecGetEmulatorName |
out_literals.txt:6670-6671 |
采集设备指纹 + 模拟器探测 |
safeComm / safeCommFromServer / safeCommToServerV30 / safeCommToServerByteV30 |
out_literals.txt:17414-17418 |
与服务端的安全(防篡改)通信通道 |
get_SC_DATA_TAMPERED / set_SC_DATA_TAMPERED |
out_identifiers.txt:11681,11700 |
数据篡改标志位 |
SC_DATA_TAMPERED |
out_identifiers.txt:87651, out_literals.txt:8104 |
篡改判定常量 |
AntiCheatResult、v16@?0@"AntiCheatResult"8 |
unity.strings:55367,91731 |
ObjC 方法类型编码(返回 AntiCheatResult) |
关键文件 Data/Managed/Metadata/gm_NTESCSLLVMPASSKEY.dat:文件名直指 NetEase(NTES)+ IL2CPP LLVM 混淆 pass key,与 global-metadata.dat 同尺寸(9,902,340 B)。其 SHA-256 与 artifacts/global-metadata.dec.dat 完全相同(10257396bf…6097e),说明该文件内容即解密后的元数据(可能是分析过程回写产物,也可能是 NetEase 元数据解密/校验所需的密钥流),两种可能均指向 NetEase 对 IL2CPP 元数据做了自定义加密/混淆(原始 global-metadata.dat 哈希 edb4a80e…86e98 ≠ 解密后 2997a109…6c3b9)。同目录另有 key612.bin / key612_mode.bin(各 612 B,内容呈 XOR 混淆字节)。
2.4 未命中的探测(重要结论)
在本包中未发现下列常见安卓侧/PC 侧作弊工具的直接特征串:frida、xposed、libhooker、cycript、gameguardian、speedhack、mudfish,也未发现中文字面串 模拟器、外挂、作弊。iOS 侧的反作弊由 NetEase NetSec(原生/托管混合) 承担,检测逻辑多在 IL2CPP 元数据混淆后,字符串层仅暴露 API 名称。
3. 文件 / 资源完整性校验#
| 精确字符串 | 来源 | 上下文 / 判定 |
|---|---|---|
Invalid MAC - file may have been tampered with! |
out_literals.txt:5458 |
并非游戏资源校验。其前后紧邻 Invalid DHValidationParms、Invalid DSA parameters、Invalid Julian day、Invalid OID 等,属 BouncyCastle 加密库(CMS/OpenPGP MAC 校验)文案。此处 MAC = Message Authentication Code,不是资产完整性提示,属"误报诱饵"。 |
SC_DATA_TAMPERED / get_SC_DATA_TAMPERED / set_SC_DATA_TAMPERED |
out_literals.txt:8104、out_identifiers.txt:11681/11700/87651 |
真正的篡改标志,属 NetEase SafeCommResult(NetEase_SafeCommResultWrap-*)。 |
VersionManifestChecker(类型) |
out_identifiers.txt:21240 |
资源版本清单校验器 |
GetManifestMd5PrefsKey、GetSaveMd5、SetSaveMd5、OnGetManifestCompleted、Run |
out_identifiers.txt:29022-29026 |
以 MD5 比对清单、结果落盘到 Prefs |
CheckVersionManifest、GetVersionManifestName、GetVersionManifestPath |
out_identifiers.txt:7287,23306,23307 |
清单检查入口 |
fileMd5、_fileMd5、_requestResourceMd5、cg_request_resource_md5、resource md5 = %@ |
unity.strings:36378,40474,41482,62149,62201 |
下载资源逐个 MD5 校验 |
Bad CRC32 in GZIP trailer. (actual({0:X8})!=expected({1:X8})) |
out_literals.txt:2374 |
GZIP 解压完整性 |
Mono.Security.*(ASN1、PKCS1、RSAManaged…) |
out_types.txt:2797-2813 |
托管加密栈 |
判定:资源完整性采用 MD5 清单校验(VersionManifestChecker)+ 单文件 MD5,属"完整性/防损坏"级别(MD5 可碰撞,非抗攻击的密码学防篡改)。真正具备运行时防篡改语义的是 NetEase SafeComm 通道与 SC_DATA_TAMPERED 标志。
4. 证书固定 / TLS#
| 检索项 | 结果 | 判定 |
|---|---|---|
pinning / SSLPinningMode / AFSecurityPolicy(在 UnityFramework 字符串) |
仅 policyWithPinningMode:(unity.strings:48015),无 AFSecurityPolicy、SSLPinningMode、pinnedCertificates 等类/属性可打印名 |
AFNetworking 被链接且具备 pinning 能力,但二进制字符串层面无配置痕迹 |
pin / SPKI / public-key-pin / sha256/ / TrustKit / pin-set |
全部未命中(out_literals.txt / out_identifiers.txt 仅命中无关的 thread_pinning_ref) |
无真实 HPKP/SPKI 固定实现 |
AFSecurityPolicy 符号 |
在 AFNetworking 二进制中作为局部符号存在:+[AFSecurityPolicy policyWithPinningMode:]、certificatesInBundle:、keyPathsForValuesAffectingPinnedPublicKeys、-[AFSecurityPolicy SSLPinningMode] |
能力存在,但无内嵌证书/公钥指纹,默认模式为 None |
| TLS 栈 | mono_btls_x509_*(Mono/BoringSSL)、mbedtls_x509_crt_parse_der、Best.HTTP.SecureProtocol.Org.BouncyCastle.* |
使用系统信任链(BoringSSL/mbedTLS),BouncyCastle 仅作密码学原语(含 X509.Allow_Non-DER_TBSCert、Rsa.MaxSize 等开关) |
结论:该包未实现真正的证书固定(certificate pinning)。TLS 完全依赖系统信任链;AFNetworking 的 AFSecurityPolicy 仅作为未配置的通用能力存在,未见硬编码 pin/cert。所谓 "pinning/SPKI" 命中均为第三方网络库样板(Mono/BoringSSL/BouncyCastle),非游戏自研防中间人实现。
5. IFix 热更新(HotFix)#
| 证据 | 来源 | 含义 |
|---|---|---|
IFix.Core.dll |
Data/ScriptingAssemblies.json(汇编清单内) |
IFix 运行时热更新程序集确实随包分发 |
##### IFix.Core.dll (41 types) + IFix.Core.*(PatchManager、VirtualMachine、ILFixDynamicMethodWrapper、WrappersManager、SimpleVirtualMachineBuilder、Instruction、EvaluationStackOperation、ExternInvoker…) |
out_types.txt / out_identifiers.txt:24491 等 |
一个可用于运行时替换 IL 方法的虚拟机 |
IFix.IDMAP、IFix.ILFixInterfaceBridge、IFix.RedirectTo. |
out_literals.txt:5210-5212 |
补丁重定向与桥接 |
./Assets/Temp/ilpatch/{0}.patch.bytes、{0}.patch、iluapatch、luapatch、toluapatch |
out_literals.txt:733,18939,16380,18238,17019 |
IL 补丁文件下载/加载路径 |
EnableHotFix、CheckHotFix、DownloadHotFixVersion、GetHotFixUrl、HotFixConfig、HotFixAll、HotFixDirName、DownloadHotFix、ShowHotFixTip |
out_identifiers.txt / out_literals.txt |
完整的热更新流水线(配置→检查→下载→应用) |
curHotFixPackageMD5、curHotFixPackageSize |
out_literals.txt:13703-13704 |
补丁包 MD5/大小校验 |
运行时风险:
1. 动态代码注入:IFix 在运行时下载 .patch.bytes 并由 VirtualMachine 执行/替换已编译的 IL 方法,等于可远程更新游戏逻辑,绕过 App Store 静态审核与签名保险。
2. 远程任意行为面:热更通道一旦被劫持/中间人(结合第 4 节无 pinning),可向客户端注入任意逻辑。
3. 与反作弊协同:EnableHotFix/HotFixAll 受服务端配置控制;官方借此快速修补逻辑,但也放大了"补丁完整性校验"的重要性(当前仅 MD5 校验,防篡改强度有限)。
4. Data/Managed/Metadata/ 下存在 global-metadata.dat(加密)与 gm_NTESCSLLVMPASSKEY.dat,说明 IL2CPP 元数据本身被 NetEase 二次加密,静态分析须先解密元数据(本包已提供解密产物)。
6. 第三方 SDK 二进制:内嵌密钥 / 端点 / 签名校验#
| SDK 框架 | 大小 | 显著字符串 / 密钥 / 端点 | 签名/校验能力 |
|---|---|---|---|
CrashSight |
1.86 MB | startWithAppId:、secretID/secretKey/token、cscos_HMACHexsha1:secret:、q-sign-algorithm=sha1&q-ak=…&q-signature=…、%@.%@.%@.amazonaws.com、CSCOSAuthentationCreator signatureForData:withCredential:err:、com.crashsight.persistent.*.errorsignature |
有:腾讯云 COS 上传使用 HMAC-SHA1 签名(secretID/secretKey 运行时下发,tmpSecretId/tmpSecretKey/sessionToken) |
AdjustSdk |
887 KB | https://analytics.adjust.com、https://consent.adjust.com、https://gdpr.adjust.com、https://subscription.adjust.com、https://ssrv.adjust.com、appToken、environment、Missing signature library, SDK can't be initialised |
依赖 AdjustSigSdk |
AdjustSigSdk |
581 KB | ADJSigner、sign:withActivityKind:withSdkVersion:、sign:withExtraParams:withOutputParams:、Signature %@,%@,%@,%@,%@ |
有:专门做 Adjust 请求签名 |
UnionOpenPlatformCore(字节跳动) |
3.46 MB | btd_HMACMD5WithKey:andData:、btd_md5String、TTMD5SumGetter、UnionOpenPlatformDouyin、DouyinOpenSDK、douyin_key、webcast-open.douyin.com、http://toblog.ctobsnssdk.com…device_register/、RangersAPM…、device_id、/var/lib/dpkg/info/mobilesubstrate.md5sums |
有:HMAC-MD5 请求签名;含越狱检测(dpkg/mobilesubstrate) |
UnionOpenPlatformDataLink |
90 KB | 数据链路桥接 | — |
BDASignalSDK |
118 KB | https://analytics.oceanengine.com/sdk/app、https://dig.bdurl.net/q?host=analytics.oceanengine.com…、kBDASignalClickid、BDASignalHeader |
设备信号采集 |
ThinkingSDK |
942 KB | appid=%@&source=client&dryRun=%d&deviceId=%@&data=%@、TDSecretKey、customSecretKey、innerSecretKey、instanceToken |
埋点上报(含秘钥字段) |
ThinkingDataCore |
164 KB | 核心工具 | — |
AFNetworking |
460 KB | AFSecurityPolicy(policyWithPinningMode: 等局部符号) |
标准网络库,未见 pin 配置 |
Protobuf |
934 KB | GPBMessageSignatureProtocol |
仅 protobuf |
FirebaseNIMessInstallation |
2.98 MB | Firebase Installation / FID | — |
appex CGPushServerExtension:https://logagent-wf.centurygame.com/log(推送投递日志上报)、CenturyGamePushData、sdk.push.eventhook、caffeinePushEventReport:event:。
App 级明文配置中的内嵌秘密(非二进制,但同属二进制外泄面) ddCommon.json:
- adjust(长 hex token)、constants、secret、secret_android_*、rum.production/sandbox、smarthelp.*、tga、umbra_gateway.*(均为超长 hex 串,疑为加密后的密钥/开关);
- helpshift: 8c855fd7c4cc6b899e2c64f37ecc65b3(32-hex);
- 明文 ID:game_id 20173、bugly_id 341c16d88c、wechat_app_id wx32374c9038cf9759、douyin_client_key awebivvetg90lv93、access_id 1580019064、access_key AQC2K08T7CLS;
- UOPSDKConfig.json:app_id 789841(UnionOpenPlatform)。
- Info.plist 中的 URL scheme 亦暴露微信 AppID(wx32374c9038cf9759)与抖音 key(awebivvetg90lv93)。
7. 风险结论#
- 壳/加密:三个主二进制
cryptid=0(已解密),但 IL2CPP 元数据被 NetEase 二次加密/混淆(global-metadata.dat原始哈希 ≠ 解密后;gm_NTESCSLLVMPASSKEY.dat/key612*.bin佐证),静态分析必须依赖解密产物。 - 反作弊=网易 NetSec/易盾:反篡改核心是
NetEase.NetSecProtect+SafeComm(V30 加密信道)+SC_DATA_TAMPERED标志;具备设备指纹采集(NetSecGetCollectDataHelper)与模拟器探测(NetSecGetEmulatorName)。 - 越狱/调试检测齐备:完整 Cydia/Substrate 路径集、
isJailBreak、jailbrokenStatus属性、isDebuggerAttached/isDebuggerAttachedToConsole,并在 UnityFramework 依赖ptrace/sysctl。 - 完整性校验偏弱:
VersionManifestChecker与资源校验使用 MD5(非抗攻击),"Invalid MAC…tampered with" 实为 BouncyCastle 库文案,非真实资产防篡改。 - 无真实证书固定:TLS 依赖系统信任链,AFNetworking 的 pinning 能力未被配置;结合 IFix 运行时 IL 热更(
./Assets/Temp/ilpatch/{0}.patch.bytes),存在热更通道被中间人劫持后注入代码的残余风险面。 - 内嵌凭据面广:
ddCommon.json、Info.plist 明文暴露微信/抖音/UnionOpenPlatform/COS 等标识与密钥密文;CrashSight 的 COS 上传、Adjust/字节 SDK 的 HMAC 签名在运行时下发密钥。
05 · 网络端点、配置文件与硬编码密钥分析#
本章基于已脱壳的 com.gof.china 1.15.6(无尽冬日 / Whiteout Survival CN)IPA 及其预抽取产物,梳理对外网络端点、配置内嵌字段、密钥/标识符定性、构建与供应链残留四类信息。
数据来源(只读、离线):
| 来源 | 路径 | 说明 |
|---|---|---|
| IL2CPP 字面量 | artifacts/out_literals.txt |
19,293 行字符串常量(含 C# 层 URL) |
| IL2CPP 标识符 | artifacts/out_identifiers.txt |
88,084 个符号名 |
| 应用包 | extracted/Payload/whiteoutsurvivalcn.app/ |
ddCommon.json / UOPSDKConfig.json / Info.plist / install/ |
| Lua 热更包 | Data/Raw/data/luapackage_{32,64}_*.unity3d |
游戏逻辑层(运行时拼接)URL 片段 |
说明:C#/原生层端点来自 out_literals.txt;Lua 层端点在热更包中以字符串拼接模板形式出现(如 https://gofcn-gm-%s-api-prod.campfiregames.cn),本文按其在字节码中的确切片段(fragment)记录,不做臆测补全。
5.1 端点清单(按用途分类)#
5.1.1 游戏资源 / CDN(全部 HTTPS)
| # | 端点(原文) | 用途 | 备注 |
|---|---|---|---|
| 1 | https://gofcn-prod-cdn.campfiregames.cn/RELEASE/{0}/{1}/ |
生产 CDN 资源根 | 占位符 {0}{1} 为版本/分包 |
| 2 | https://gofcn-prod-cdn.campfiregames.cn/RELEASE/WebUrl_2/Web{0}.txt |
生产 WebUrl 清单 | 网页/活动资源清单 |
| 3 | https://gofcn-prod-cdn.centurygames.cn/RELEASE/{0}/{1}/ |
生产 CDN 备用域 | 世纪游戏主域 centurygames.cn |
| 4 | https://gofcn-qa-cdn.campfiregames.cn/RELEASE/{0}/{1}/ |
QA CDN | 随包发布,测试环境 |
| 5 | https://gofcn-qa-cdn.campfiregames.cn/TEST/{0}/ |
QA CDN 测试 | |
| 6 | https://gofcn-qa-cdn-1322302784.cos.ap-shanghai.myqcloud.com/RELEASE/{0}/{1}/ |
腾讯云 COS 生产镜像 | 桶 ID 1322302784,上海 |
| 7 | https://gofcn-qa-cdn-1322302784.cos.ap-shanghai.myqcloud.com/TEST/WebUrl/Web{0}.txt |
COS 测试清单 | |
| 8 | https://gofcn-qa-cdn-1322302784.cos.ap-shanghai.myqcloud.com/TEST/{0}/ |
COS 测试资源 | |
| 9 | https://gofcn-qa-cdn-1322302784.cos.ap-shanghai.myqcloud.com/INNER/WebUrl/Web{0}.txt |
COS INNER(内部)清单 | ⚠ 内部环境标识 INNER |
| 10 | https://gofcn-prod-avatar-cdn.campfiregames.cn/static/app/...(Lua 片段) |
生产头像 CDN | |
| 11 | https://gof-s3-user-avatar.s3.us-west-2.amazonaws.com/(Lua 片段) |
用户头像 S3 桶 | 美西 us-west-2 |
| 12 | https://1254330885.cos.ap-guangzhou.myqcloud.com/...(Lua 片段) |
另一腾讯云 COS 桶 | 桶 1254330885,广州 |
| 13 | ac192625a0e1c417b8e09811b9449245-50cb9e3108c40714.elb.us-west-2.amazonaws.com(Lua 片段) |
AWS Classic ELB(后端服务) | 名称含 ELB ID,非 CDN |
5.1.2 配置 / 版本 / 设备(全部 HTTPS)
| # | 端点(原文) | 用途 | 备注 |
|---|---|---|---|
| 14 | https://platform-config-prod.centurygame.com/config.json |
平台配置下发(生产) | 与 ddCommon.json 同名基线配置 |
| 15 | https://platform-config-sandbox.centurygame.com/config.json |
平台配置下发(沙箱) | |
| 16 | https://clientversion.diandian.info/snapshot/ |
客户端版本 / 快照 接口 | ⚠ 见 5.1.7 |
| 17 | https://devcenter.campfiregames.cn/api/devices |
设备注册 / 开发者中心 API | 采集设备指纹相关 |
5.1.3 GM / 运营 / 后台 API(⚠ 重点标记)
| # | 端点(原文) | 环境 | 备注 |
|---|---|---|---|
| 18 | https://gofcn-gm-api-prod.campfiregames.cn |
生产 | GM API 根 |
| 19 | https://gofcn-gm-api-test-out.campfiregames.cn |
测试(外网) | 测试环境地址随生产包发布 |
| 20 | https://gofcn-gm-api.diandian.info |
内部/历史域名 | 旧域名 diandian.info |
| 21 | https://gofcn-gm-api-staging.campfiregames.cn(Lua) |
Staging | 灰度环境地址 |
| 22 | https://gofcn-gm-{h\|r}*-api-prod.campfiregames.cn/api/...(Lua 片段) |
生产 | 拼接模板:/api/.../record、/api/user/... |
| 23 | https://gof-ru-f*-api-formal.echofungames.com/api/.../record(Lua 片段) |
俄区正式 | 关联域 echofungames.com |
| 24 | https://{?}-list-api.100topia.cn/api/g...rSetting(Lua 片段) |
— | 关联域 100topia.cn |
为何值得关注: GM/运营 API 域名(gofcn-gm-api-*)出现在 C# 字面量与 Lua 字节码中,意味着客户端具备调用 GM 接口的能力;同时生产、测试(-test-out)、staging 三套地址全部随线上包发布,任意一套被盗用即可作为横向探测入口。diandian.info(旧法人域)与 echofungames.com、100topia.cn(关联域)暴露了基础设施的历史与关联资产。
5.1.4 第三方 SDK / 社交 / 广告(HTTPS)
| # | 端点(原文) | 归属 | 备注 |
|---|---|---|---|
| 25 | NSAdvertisingAttributionReportEndpoint = https://adjust-skadnetwork.com |
Adjust | ⚠ 末尾含 零宽空格 U+200B(e2 80 8b) |
| 26 | https://srdebugger.stompyrobot.uk/report/submit |
SRDebugger | 运行时调试面板上报端点 |
| 27 | https://mp.weixin.qq.com/mp/homepage?__biz=MzkwMDY3NDc4MQ==&hid=1&sn=78c72e284740ab05a4441398910e3b4e |
微信 | 公众号主页 |
| 28 | https://tieba.baidu.com/f?ie=utf-8&kw=%E6%97%A0%E5%B0%BD%E5%86%AC%E6%97%A5 |
百度贴吧 | “无尽冬日”吧 |
| 29 | https://www.xiaohongshu.com/user/profile/5ea676d500000000010065f3?... |
小红书 | 官方账号 |
| 30 | https://l.taptap.cn/tIZHXGwE?...(Lua 片段) |
TapTap | 渠道短链 |
| 31 | https://posts.tenpay.com(Lua 片段) |
财付通 | 支付相关 |
| 32 | https://www.toponad.com/(Lua 片段) |
TopOn | 广告聚合 |
| 33 | https://legal.*centurygames.cn/privacy_wjdr.html(Lua 片段) |
世纪游戏 | 隐私政策(CN) |
| 34 | https://www.centurygames.com/privacy-policy/(Lua 片段) |
世纪游戏 | 隐私政策(国际) |
| 35 | https://...gg/mKKJemAD5a(Lua 片段,前缀为 https://d) |
Discord | 社区邀请链接 |
| 36 | https://mmoc.qpic.cn/wechat...(Lua 片段) |
腾讯 | 微信图片 CDN(mmoc + .qpic.cn/wechat) |
| 37 | tpns.sh.tencent.com(ddCommon.json 的 XG_SERVER_SUFFIX) |
腾讯 TPNS 推送 | 主域无 scheme |
LSApplicationQueriesSchemes 另声明了 fb / fb-messenger / whatsapp / weixin / weixinULAPI / weixinURLParamsAPI / douyinopensdk / douyinliteopensdk / douyinshare / snssdk1128 / weibosdk,印证集成了微信、抖音、微博等分享/登录 SDK。
5.1.5 框架 / 文档噪声(HTTP 明文,非运行端点)
以下均为第三方库内嵌注释/命名空间,与本游戏业务无关,且多为 HTTP 明文:
http://www.w3.org/2000/09/xmldsig#*、http://www.w3.org/2001/04/xmldsig-more#*、http://www.w3.org/2001/04/xmlenc#*(XML 签名/加密命名空间)http://www.root-motion.com/finalikdox/html/*(Final IK 文档)http://forum.unity3d.com/threads/final-ik-...222685/https://docs.unity3d.com/Manual/LogFiles.html、https://groups.google.com/forum/#!forum/final-ikhttps://github.com/mono/mono/issues/12880https://bitbucket.org/sirenix/odin-inspector/issues/526(Odin)https://apps.apple.com/cn/app/.../id6478492012(本作 App Store 页)https://localhost/(占位)https://bestdocshub.pages.dev/HTTP/getting-started/downloads/http://www.apple.com/DTDs/PropertyList-1.0.dtd(各Info.plistDTD)
5.1.6 HTTP 明文 vs HTTPS 汇总
- 业务/配置/GM/第三方 SDK 端点:100% 为 HTTPS。
- 明文
http://仅出现在框架文档噪声与 plist DTD 中,不构成运行期明文传输。 - 值得注意:
Info.plist未声明NSAppTransportSecurity例外(grep -c= 0),即系统 ATS 默认拦截明文 HTTP——与“真实端点全部 HTTPS”一致。
5.1.7 snapshot 端点专项
https://clientversion.diandian.info/snapshot/
- 同时存在字面量
/snapshot(out_literals.txt:841,无 scheme 的路径片段)。 - 语义为客户端版本/资源快照(版本比对与增量更新入口)。以旧法人域
diandian.info承载,说明该更新链路沿用较早基础设施;/snapshot/未带签名参数,若服务端鉴权不足,可能成为版本枚举/降级攻击的探测面(仅从端点形态判断,未做验证)。
5.2 ddCommon.json 全量分析#
路径:extracted/Payload/whiteoutsurvivalcn.app/ddCommon.json(应用根目录随包发布,MD5 7cef956efab74190b927f30952cc5b40)。该文件是明文 JSON,含有大量标识符与加密配置块。
5.2.1 顶层字段
| 字段 | 值(精确引用) | 定性 |
|---|---|---|
id |
3281 |
平台/应用内部 ID(非密钥) |
adjust |
208 字节 hex(见 5.3) | 加密块 |
constants |
1792 字节 hex | 加密块(最大,含常量/地址) |
helpshift / helpshift_ios |
8c855fd7c4cc6b899e2c64f37ecc65b3(32 hex) |
Helpshift 应用 Key(半公开,实为 SDK 标识) |
secret |
208 字节 hex | 加密块 |
secret_android_game |
80 字节 hex | 加密块 |
secret_android_production |
48 字节 hex | 加密块 |
secret_android_sandbox |
48 字节 hex | 加密块 |
tga |
224 字节 hex | 加密块(TGA 数据上报配置) |
rum.production / rum.sandbox |
各 704 字节 hex | 加密块(RUM 监控) |
smarthelp.production / smarthelp.sandbox |
各 288 字节 hex | 加密块(智能客服) |
umbra_gateway.production / sandbox |
各 496 字节 hex | 加密块(Unity Umbra 网关配置) |
unity_config |
1324 字符 Base64 | 加密块 |
5.2.2 platform.ios(与 android / oh 并列)
| 字段 | 值(精确引用) | 定性 |
|---|---|---|
bugly_id |
341c16d88c |
公开标识(腾讯 Bugly AppID,android 侧为 1ea45351f9) |
china |
true |
环境标志 |
game_id |
20173 |
公开标识(世纪游戏统一 game_id;URL Scheme、appex 均复用) |
is_offline_games |
false |
环境标志 |
douyin_client_key |
awebivvetg90lv93 |
半公开标识(抖音开放平台 Client Key;同时作为 URL Scheme) |
wechat_app_id |
wx32374c9038cf9759 |
半公开标识(微信开放平台 AppID;同时作为 URL Scheme) |
tga_inited_early |
true |
开关 |
tga_encryption_info |
432 字节 hex(与 android / oh 完全一致) | 加密块(TGA 上报加密材料) |
5.2.3 platform.android / platform.oh 额外字段
| 字段 | 值(精确引用) | 定性 |
|---|---|---|
android_package_name / oh_package_name |
com.gof.china |
包名 |
XG_SERVER_SUFFIX |
tpns.sh.tencent.com |
腾讯 TPNS 推送主域 |
access_id |
1580019064 |
推送凭据 ID(腾讯 TPNS AccessID) |
access_key |
AQC2K08T7CLS |
⚠ 推送密钥(真密钥),明文 12 字符 |
关于“admin keys”:ddCommon.json 中不存在名为 admin 的字段。与“管理/服务端凭据”语义最接近的是腾讯 TPNS 推送的 access_id / access_key,其中 access_key = AQC2K08T7CLS 属应保密的密钥(虽为推送通道,泄露可被用于伪造推送/盗刷配额)。
5.2.4 明文评述
game_id(20173)、bugly_id、wechat_app_id、douyin_client_key 均为客户端公开/半公开标识,本身泄露风险低(受平台侧签名与服务端校验约束)。真正的敏感点是腾讯推送 access_key 与全部 hex/base64 加密块中的对称配置。
5.3 不透明字段(hex / base64)的格式与熵评估#
所有 hex 块均为偶数字节长度的十六进制串(首字节均为 cfab7bf2.../995ea76d... 等固定前缀),Base64 块 unity_config 以 B19cZy+w... 开头,末尾以 = 填充。逐字段字节熵(Shannon,bits/byte)如下:
| 字段 | 编码 | 字节长度 | 首 8 字节 hex | 字节熵 (bits/byte) |
|---|---|---|---|---|
secret_android_production |
hex | 48 | 5970f990f745e2af |
5.335 |
secret_android_sandbox |
hex | 48 | 5970f990f745e2af |
5.418 |
secret_android_game |
hex | 80 | 2564594dae877f8a |
5.953 |
smarthelp.production |
hex | 288 | 89ca80f5e27dc345 |
7.162 |
smarthelp.sandbox |
hex | 288 | 011c999e7ae87462 |
7.240 |
adjust |
hex | 208 | c1540cc8f7854eef |
7.036 |
secret |
hex | 208 | 2564594dae877f8a |
7.003 |
tga |
hex | 224 | 387bf285c58ec55b |
7.163 |
platform.*.tga_encryption_info |
hex | 432 | cfab7bf27c7e5a0d |
7.474 |
umbra_gateway.production |
hex | 496 | 995ea76dc7199427 |
7.606 |
umbra_gateway.sandbox |
hex | 496 | 995ea76dc7199427 |
7.591 |
rum.production |
hex | 704 | cf542ca2130eae44 |
7.734 |
rum.sandbox |
hex | 704 | 80e4a71af79b0119 |
7.752 |
constants |
hex | 1792 | 8169e58dcbbf1a5a |
7.902 |
unity_config |
base64 | 992 | B19cZy+wVkuCtJNY(解码后) |
7.766 |
格式判定(只做结构描述,不尝试破解):
- 高熵块(≥7.0 bits/byte)符合 AES/分组加密密文或高熵密钥材料 的特征;
uint8分布均匀,无可见明文结构。 - 低-中熵块(
secret_android_*5.3–6.0)熵偏低,提示其内部包含重复的固定前缀/版本段(例如secret_android_production与sandbox前 8 字节完全相同),可能是“加密后的字符串 + 固定元数据”的混合体。 - 多组字段共享相同前缀,说明它们是同一密钥体系/同一序列化器产出(例如
secret与secret_android_game均以2564594d...开头;umbra_gateway两环境前缀相同、仅尾部不同)。 tga_encryption_info在 android/ios/oh 三平台字节完全一致,且与被引用为顶层的tga(224B)不同——说明 tga 存在平台无关的公共加密材料。unity_config为 Base64 包裹的密文/二进制配置(含+ / =,解码后 992 字节、熵 7.77)。
这些块预计由世纪游戏 DD SDK(Unity.dd.sdk.scripts.*)在运行时解密使用;本报告不对其做密码学破解,仅记录编码、长度、前缀与熵,作为“存在加密配置”的客观证据。
5.4 UOPSDKConfig.json#
路径:extracted/Payload/whiteoutsurvivalcn.app/UOPSDKConfig.json(MD5 c85fe6aa0a1d58aeb9c6a80b9f1379c0),全文:
{
"app_id": "789841",
"osdk_type": "Omnichannel_ActivityLink",
"client_key": {
"douyin_key": ""
},
"debug_mode": false
}
| 字段 | 值 | 定性 |
|---|---|---|
app_id |
789841 |
抖音“UOP/巨量”渠道 AppID(公开标识) |
osdk_type |
Omnichannel_ActivityLink |
全渠道活动链接能力开关 |
client_key.douyin_key |
空字符串 | 客户端未内嵌密钥(服务端配置,良好实践) |
debug_mode |
false |
生产关闭 |
结论:UOPSDK 的敏感 douyin_key 为空,未在包内泄露;此为正确做法,可作为对照项。
5.5 密钥定性汇总:真密钥 vs 公开标识#
| 类型 | 字段 / 值 | 判定 | 依据 |
|---|---|---|---|
| ✅ 公开标识 | game_id=20173、bugly_id=341c16d88c、wechat_app_id=wx32374c9038cf9759、douyin_client_key=awebivvetg90lv93、app_id=789841 |
公开/半公开 | 需配合 AppSecret/签名并由服务端校验,单独泄露风险低 |
| ✅ 公开标识 | helpshift=8c855fd7c4cc6b899e2c64f37ecc65b3、id=3281 |
SDK/应用标识 | Helpshift Key 属 SDK 侧公开字段 |
| ⚠ 真密钥 | platform.android/oh.access_key = AQC2K08T7CLS |
应保密 | 腾讯 TPNS 推送密钥,明文内嵌 |
| ⚠ 真密钥(不透明) | adjust / constants / secret / secret_android_* / tga / tga_encryption_info / rum.* / smarthelp.* / umbra_gateway.* / unity_config |
加密的对称配置 | 高熵密文/Base64,运行时解密,内容不可直接读取 |
| ⚠ 敏感地址 | gofcn-gm-api-prod/-test-out/.diandian.info、gofcn-gm-api-staging |
内部/后台入口 | 生产包内同时暴露 prod/test/staging |
要点:唯一明文真密钥是腾讯 TPNS 的 access_key;其余敏感材料均被加密包裹,属于“有防护但仍随包分发”的配置。
5.6 构建 / 供应链残留(随生产 IPA 发布)#
以下文件被打包进了线上 IPA,属于典型的“开发/CI 产物泄漏”。
5.6.1 install/buildIpa.sh
projName=$1
projPath=$2
codeSignType=$3
provision=$4
#provision="60a0e608-4a3b-42cb-8191-ae06c24af1e2"
...
xcodebuild archive -project $projName.xcodeproj -scheme $projName -configuration Release \
-archivePath $projPath/output/$projName.xcarchive \
CODE_SIGN_IDENTITY="$codeSign" PROVISIONING_PROFILE="$provision" > output/archiveLog.txt
- 泄露CI 打包流程与签名方式(
iPhone Distribution)。 - 注释中残留配置描述文件 UUID:
60a0e608-4a3b-42cb-8191-ae06c24af1e2。
5.6.2 install/autoUpdateRepoPublic.sh
CGSRepo="bitbucket.org/funplus/centurygamesdkpodspec.git"
...
sourcePath=$(grep -E 'source.*oauth2:' $podfilePath | awk '{print $NF}' | sed ...)
addRepoCommand="pod repo add CenturyGameSDKPodSpecPublic ${sourcePath}"
- 泄露内部 Bitbucket 仓库:
bitbucket.org/funplus/centurygamesdkpodspec.git(组织funplus,与世纪游戏/点点同源)。 - 脚本从
Podfile的source ... oauth2:行提取 CocoaPods 私有源地址(含 oauth2 令牌位)——即私有 spec 源使用带令牌的oauth2:形式,令牌不在此脚本内但指明其存在。 - 涉及缓存清理对象:
CenturyGameiOSCGIDWebResource(CGID Web 资源 Pod)。
5.6.3 CenturyGameResources.bundle/merge.sh + merge_infoplist.py
merge.sh:构建期脚本,调用python .../CenturyGameResources.bundle/merge_infoplist.py $projectName $config_pathmerge_infoplist.py:读ddCommon.json的platform.ios段,在打包时动态改写Info.plist,写入CFBundleURLSchemes(centurygame=com.diandian.logger.<game_id>、wechat、QQ、Facebook、Google、Twitter、Naver 等)与LSApplicationQueriesSchemes。- 代码为 Python 2 语法(
print语句、has_key),说明构建链较旧。 - 依赖
mopub-sdk-id.json(SKAdNetwork 列表)注入SKAdNetworkItems。
同一对脚本在 CenturyGameResources.bundle/ 与 Frameworks/UnityFramework.framework/CenturyGameResources.bundle/ 各存一份(重复随包发布)。
5.6.4 SC_Info/ 内容
| 文件 | 大小 | 说明 |
|---|---|---|
Manifest.plist |
1744 B | SinfPaths + 16 条 SinfReplicationPaths(列出所有 Frameworks/appex 的签名副本路径) |
whiteoutsurvivalcn.sinf |
1056 B | 主二进制签名封装,头 sinf/frma=game/schm=itun(FairPlay 结构) |
whiteoutsurvivalcn.supf / .v5.supf |
1020 / 1000 B | 公平游戏签名记录 |
whiteoutsurvivalcn.supp / .v4.supp |
1636 / 1616 B | 签名属性 |
whiteoutsurvivalcn.supx |
80 B | 签名扩展 |
- 主二进制与
UnityFramework的cryptid均为 0(已脱壳),因此SC_Info仅为 App Store 加密时代的残留容器,本身不含可解密密钥。 Manifest.plist完整暴露了内部 framework 构成(Adjust、CrashSight、ThinkingSDK、BDASignalSDK、UnionOpenPlatform、Firebase 等)。
5.6.5 Payload/decrypt.day
und3fined
- 内容为 9 字节 ASCII
und3fined,位于Payload/decrypt.day。 - 这是第三方脱壳/解密工具(“und3fined”)在重打包时留下的标记文件,证明该 IPA 已被第三方工具处理过,并非官方发布产物。此文件本身不携带密钥,但标记了供应链中间环节。
综合 5.6.1–5.6.5:构建脚本、CI 凭据线索、私有 repo 地址、签名残留与脱壳标记均随生产 IPA 一同分发,属于可被攻击者直接利用的“信息泄露面”。
5.7 主机 / 格式清单(IP、端口、ws/wss)#
5.7.1 硬编码 IP 与端口
- 未发现真实业务 IP 或端口。
out_literals.txt中形如1.1.3.1、1.2.643.2的均为 ASN.1/X.509 OID;:1987、:1988、:1991、:13321分别来自字符集名(ISO_8859-3:1988等)与版本号,非网络端口。 - 仅见占位值
0.0.0.0与https://localhost/(框架占位)。 - 游戏服务器地址显然不硬编码于可读字面量,而是经
platform-config/clientversion/ 加密umbra_gateway/constants在运行时下发。
5.7.2 ws:// / wss://(实时通信)
- 字面量中不存在硬编码的
ws:///wss://端点(对out_literals.txt全文匹配wss?://仅得 scheme 常量)。 - 存在的仅是协议相关常量/报错文本:
- 字面量:
Only Uris starting with 'ws://' or 'wss://' are supported. - Lua 字节码片段:在
IsWebSocket附近出现ws://与ws(属 UnityWebSocket/Mono 的 scheme 判定,非端点)。 - 实时通信能力确实存在,由以下证据确认:
Data/ScriptingAssemblies.json含UnityWebSocket.Runtime.dll;- 类型:
CenturyGame.GameFramework.WebSocketProxy(Open/SendBinary/SendMessage/SetOpenHandler...)及 Lua 包装CenturyGame_GameFramework_WebSocketProxyWrap; - 加密配置
umbra_gateway(production/sandbox) 即网关(Gateway)配置块,实时网关地址被加密隐藏,无法在不解密的前提下读出。
5.7.3 其他主机格式
dd_sdk_unity_version = 8.0.5.0-GOFCN-1(Info.plist),dd_sdk_environment_production = true——两平台环境标志。- 腾讯推送主域
tpns.sh.tencent.com(无 scheme,ddCommon.json)。
5.8 风险定级与结论#
| 风险项 | 等级 | 说明 |
|---|---|---|
明文真密钥 access_key=AQC2K08T7CLS 内嵌 |
中 | 腾讯 TPNS 推送密钥随包分发 |
| GM/运营 API(prod/test/staging)随包发布 | 中-高 | 后台入口暴露,利于横向探测 |
| 构建/供应链残留(Bitbucket 私有 repo、oauth2 线索、CI 脚本、脱壳标记) | 中 | 泄露组织与基础设施结构,可用于社工/供应链攻击 |
| 大量 hex/Base64 加密配置(secret/adjust/tga/umbra_gateway 等) | 低-中 | 有加密保护,构成“信息防御”但仍是攻击面 |
snapshot 版本接口(旧域、无签名迹象) |
低-中 | 可能被用于版本枚举/降级探测(未验证) |
| 明文 HTTP 业务端点 | 无 | 业务端点全 HTTPS,且无 ATS 例外 |
| 硬编码 IP / 端口 / ws 端点 | 无 | 未发现 |
结论: 该版本没有在可读字面量中硬编码业务服务器地址、IP 或端口,实时网关被加密(umbra_gateway)保护,基础网络传输(HTTPS + ATS 默认)较为规范;主要问题不在“网络层”,而在配置与供应链层——GM/运营多环境地址外泄、唯一明文推送密钥内嵌,以及构建脚本/私有仓库线索随 IPA 发布。建议:移除 install/、merge*.py/sh、decrypt.day 等非运行产物;将 TPNS access_key 移至服务端签发;对 gofcn-gm-api-* 与 snapshot 加强服务端鉴权与限流。
06 · IL2CPP 元数据加密还原(核心技术章节)#
本章记录本次逆向中最具技术含量的部分:绕过 global-metadata.dat 的加密保护,并从中还原出该 Unity 游戏的完整符号体系。
6.1 背景:IL2CPP 与元数据#
Unity 使用 IL2CPP 后端时,C# 程序集会被 AOT 编译为原生代码,同时把"反射所需的元数据"(类型、方法、字段、字符串字面量、程序集清单等)集中写入:
Data/Managed/Metadata/global-metadata.dat
其结构以一个固定头开始(magic 0xFAB11BAF),随后是各数据区(字符串表、方法表、字段表、类型定义表、程序集映像表……)的偏移与大小。只要能读出该文件,就能恢复出游戏的全部 C# 符号(类名/方法名/字符串常量),因此厂商常对它做加密。
标准头签名(little-endian):
AF 1B B1 FA | version(int32) | stringLiteralOffset | stringLiteralSize | ...
本样本原始文件头却是:
00000000: e14f f4a9 5c53 4c4c 564c 5041 a309 4945 .O..\SLLVLPA..IE
原始 magic 已被破坏,且整个文件呈现异常高的"可打印 ASCII"比例(73.3%)——这是周期 XOR 的典型特征(明文多为小值/0,XOR 上 ASCII 密钥后落入可打印区)。
6.2 破译过程#
步骤 1 · 自相关定位密钥周期
对密文做逐 lag 的字节自相关 P(ct[i] == ct[i+L])(numpy,采样加速)。若密文由周期密钥 XOR 生成,则当 L 为周期整数倍时,相同明文位置会重复出现相同密文,匹配率显著抬升。
结果(Top 峰值):
| lag(L) | 匹配率 |
|---|---|
| 612 | 0.384 |
| 1224 | 0.380 |
| 1836 | 0.374 |
| 3672 | 0.373 |
| 2448 | 0.370 |
所有峰值均为 612 的倍数,故真实周期整除 612。612 = 36 × 17,提示周期可能远短于 612。
步骤 2 · 相位众数恢复密钥
对每个相位 i mod 612 统计密文字节众数(明文 0x00 占主导时,密文=密钥):
key[0..15] = 4E 54 45 53 EE 6E 4C 4C 56 4D 50 41 AC AC 4B 45
N T E S . n L L V M P A . . K E
可见其高度重复,呈现 17 字节模式(NTES…LLVMPA…KEY)。结合步骤 1 的 612=36×17,判定真实周期为 17。
步骤 3 · 用已知明文校验并定钥
密钥前 4 字节 XOR 密文头应等于标准 magic AF 1B B1 FA:
E1 4F F4 A9 XOR key[0..3] = AF 1B B1 FA
=> key[0..3] = "NTES" (0x4E 0x54 0x45 0x53)
再以版本字段(应为小整数)反推 key[4..],最终得到完整 17 字节周期密钥:
NTESCSLLVMPASSKEY
( 4E 54 45 53 43 53 4C 4C 56 4D 50 41 53 53 4B 45 59 )
步骤 4 · 解密验证
以该密钥对整文件循环 XOR:
明文头: AF 1B B1 FA | 1F 00 00 00 | ...
magic 正确 version = 31
version = 31→ 合法的 IL2CPP 元数据版本;- 字符串区可完整读出(
The exception contained the following message:、Lua 运行时源码片段等); - 全文件任意位置均可检出
UnityEngine.MonoBehaviour、mscorlib、Assembly-CSharp、get_DeviceID、AntiCheatResult、DecryptLuaBytecode等符号。
结论:解密完全正确且可复现(脚本见附录)。
步骤 5 · 保护方案归因(NetEase 易盾)
密钥串前缀 NTES 与元数据中还原出的符号高度吻合,指向 网易易盾 NetSec(NetEase.NetSecProtect) 加固方案:
NetEase.NetSecProtect / NetEase.AntiCheatResult / NetEase.SafeCommResult
NetEase.HTProtectConfig / NetEase.GetTokenCallback / NetEase.RequestCmdID
InitNetSecProtect / NetSecGetEmulatorName / NetSecGetToken / NetSecGetCollectDataHelper
NTES 恰为 NetEase 的股票代码/缩写。可判定该游戏的 IL2CPP 元数据加密 + 反作弊/设备指纹 均由网易易盾 SDK 提供(NTES…PASSKEY 很可能为易盾生成的一次性/固定口令)。
⚠️ 保护强度评估:这是一处"隐藏式安全"(security by obscurity)。固定、可打印、可自相关定位的周期 XOR 密钥,在数分钟内即可离线还原,不构成有效防护,仅略微提高自动化工具(如 Il2CppDumper 直接读取)的门槛。易盾的真正价值在于其运行时反作弊(设备指纹/模拟器识别/token),而非静态加密本身。
6.3 还原结果#
解密后文件:artifacts/global-metadata.dec.dat(9,902,340 B,version 31)。
6.3.1 元数据结构统计(由文件头直接解析)
| 数据区 | 偏移 | 大小(字节) | 条目数 |
|---|---|---|---|
字符串字面量索引 stringLiteral |
0x00000100 | 154,352 | 19,294 条字面量 |
字符串字面量数据 stringLiteralData |
0x00025BF0 | 547,416 | — |
标识符字符串堆 string |
0x000AB648 | 2,170,332 | 约 88,084 个标识符 |
事件 events |
0x002BD424 | 5,280 | 220(÷24) |
属性 properties |
0x002BE8C4 | 151,760 | 7,588(÷20) |
方法 methods |
0x002E3994 | 2,616,048 | 36,334(÷72) |
参数字段默认值 parameterDefaultValues |
0x00562484 | 13,428 | — |
字段默认值 fieldDefaultValues |
0x005658F8 | 370,032 | — |
参数 parameters |
0x00609800 | 1,031,784 | — |
字段 fields |
0x00705668 | 663,672 | 55,306(÷12) |
| 泛型参数/约束/容器 | … | … | — |
类型定义 typeDefinitions |
0x0081394C | 748,968 | 8,511(÷88) |
映像 images |
0x008CA6F4 | 2,760 | 69 个程序集/映像 |
程序集 assemblies |
0x008CB1BC | 4,416 | 69 |
属性数据 attributeData / range |
… | … | — |
导出类型 exportedTypeDefinitions |
0x0097066C | 4,760 | — |
6.3.2 程序集(Images)清单(69 个)
按类型数量排序的、值得关注的自研/第三方程序集:
| 程序集 | 类型数 | 说明 |
|---|---|---|
Assembly-CSharp.dll |
2,731 | 游戏主程序集(C# 侧:框架、UI、桥接) |
mscorlib.dll |
1,287 | .NET 基础库 |
com.Tivadar.Best.HTTP.dll |
1,228 | Best HTTP/2(含 BouncyCastle 加密库) |
OdinSerializer |
215 | 序列化 |
spine-unity |
161 | 2D 骨骼动画 |
CriMw.CriWare.Runtime |
193 | CRI 音视频中间件 |
IFix.Core.dll |
41 | 热更新(IL 热补丁)框架 |
Unity.dd.sdk.scripts.*(多枚) |
— | Century Games 自研 SDK:core/account/accountcn/payment/adjust/tga/advertising/wechat/push/rate/smartHelp/helpshift/wjx/report/tools/bugly |
CenturyGame.GameFramework |
24 | 自研游戏框架(资源/Lua/AssetBundle/网络) |
UnityWebSocket.Runtime |
15 | 实时 WebSocket |
CString / FastString.Runtime |
— | 字符串处理优化 |
Debugger.dll / HdgRemoteDebugRuntime |
— | 远程调试运行时 |
其余为 Unity 各模块(UnityEngine.*)与系统库(System.*、Mono.Security、System.IO.Compression 等)。
6.3.3 业务与技术栈画像(标识符挖掘)
从 artifacts/out_identifiers.txt(88,084 个标识符)与 artifacts/out_types.txt 可还原出游戏技术栈:
① 脚本层:Lua(XLua / LuaInterface)
CenturyGame.GameFramework.LuaModule (Init/CallLua/ExecuteStringCode/…)
CenturyGame.GameFramework.LuaLoader (AddLuaBundle/GetLuaFromBundle/ReadZipFile)
CenturyGame.GameFramework.Helper.DecryptLuaBytecode / EncryptLuaBytecode / CheckLuaBytecodeHeader
LuaInterface (50 个类型)
→ 游戏玩法逻辑以 Lua 字节码形式承载,资源包内 luapackage_32/64_*.unity3d(各约 20 MB)即其容器,运行时解密执行。这意味着"上层玩法可被反编译/篡改",是关键攻击面。
② 资源层:AssetBundle + XOR 加密
CenturyGame.GameFramework.Helper.EncryptXOR / EncryptXOR1 / EncryptXOR2
CenturyGame.GameFramework.Helper.CalcEncryptKeyFromString
CenturyGame.GameFramework.AssetDataInfo.BundleEncryptOffset
CenturyGame.GameFramework.AssetBundleMgr / AssetBundleDownloadHandler / AssetHelper
CenturyGame.GameFramework.VersionManifestChecker.GetManifestMd5PrefsKey
→ 资源包采用偏移/异或加密 + MD5 清单保护。
③ 自研框架能力
CenturyGame.GameFramework.AssetModule / AtlasModule / CacheModule / AudioModule
CenturyGame.GameFramework.WebSocketProxy / BestHttpGetWork / HttpRequestWork / HttpGetAppVersion
CenturyGame.GameFramework.SDKModule.OnRequestIDFATrackingAuthorization
centurygame.IOS.* (18 个类型, iOS 原生桥接)
④ 安全与风控相关符号
AntiCheatResult
isJailbreak / isJailbroken / _jailbrokenStatus / setJailbrokenStatus:
isDebuggerAttached / isDebuggerAttachedToConsole
SC_DATA_TAMPERED ("Invalid MAC - file may have tampered with!")
Cmd_GetDeviceId / DeviceID / GetIDFA / GetIDFV / CGIDFAAuthorizationStatus
ConstDefine.DeviceID / GameConfig.DeviceID
GetManifestMd5 / CalcMd5StringFromHash / CRC32
⑤ 实时通信 / 协议
com.fpnn.rtm / com.fpnn.msgpack / com.fpnn.proto (FNNS RTM 即时通讯协议栈)
UnityWebSocket.Runtime
Protobuf (Google Protocol Buffers 3.29.5)
CenturyGame.GameFramework.BytePackHelper
6.3.4 网络端点(由字符串字面量还原,详见第 5 章)
从 19,294 条字面量中提取出的真实业务域名示例:
https://gofcn-prod-cdn.campfiregames.cn/RELEASE/ # 生产 CDN
https://gofcn-prod-cdn.centurygames.cn/RELEASE/
https://gofcn-qa-cdn.campfiregames.cn/{RELEASE,TEST}/ # QA CDN
https://gofcn-qa-cdn-1322302784.cos.ap-shanghai.myqcloud.com/... # 腾讯云 COS QA
https://platform-config-prod.centurygame.com/config.json # 平台配置
https://platform-config-sandbox.centurygame.com/config.json
https://clientversion.diandian.info/snapshot/ # 版本快照
https://devcenter.campfiregames.cn/api/devices # 设备中心
https://gofcn-gm-api-prod.campfiregames.cn # ★ GM/运营后台 API
https://gofcn-gm-api-test-out.campfiregames.cn
https://gofcn-gm-api.diandian.info
→ 其中 gofcn-gm-api*(GM 接口) 与 devcenter 属管理面域名,出现在客户端包内值得关注(虽通常另有鉴权,但域名与路径暴露扩大了攻击面)。
6.4 还原价值与影响#
- 符号级可读:还原后可直接得到所有类/方法/字段名,Unity 反编译工具(Il2CppDumper + IDA/Ghidra/Il2CppInspector)可无缝加载
global-metadata.dat,将原生代码函数与 C# 符号对应。 - 攻击面外扩:结合
DecryptLuaBytecode与BundleEncryptOffset的还原,攻击者可进一步解密资源包与 Lua 字节码,从而修改玩法数值、资源或逻辑。 - 风控可描述:
AntiCheatResult、越狱/调试检测、MD5 清单校验等机制被完整枚举,便于评估其绕过成本(多为客户端本地检测,绕过成本低)。
6.5 可复现命令#
# 1) 解包
unzip com.gof.china_1.15.6_und3fined.ipa -d extracted
# 2) 解密 global-metadata.dat(使用附录脚本,密钥 NTESCSLLVMPASSKEY)
python3 artifacts/decrypt_global_metadata.py \
extracted/Payload/whiteoutsurvivalcn.app/Data/Managed/Metadata/global-metadata.dat \
artifacts/global-metadata.dec.dat
# 3) 校验
xxd artifacts/global-metadata.dec.dat | head -1 # 期望: af1b b1fa 1f00 0000 ...
strings artifacts/global-metadata.dec.dat | grep -m1 UnityEngine.MonoBehaviour
自相关复现代码(关键片段)
import numpy as np
data = np.frombuffer(open("global-metadata.dat", "rb").read(), np.uint8)
sub = data[::7]
for L in range(1, 8193):
print(L, np.mean(sub[:-L] == sub[L:])) # 观察 612 及其倍数处的峰值
密钥恢复(众数法)
from collections import Counter
P = 612
key = bytes(Counter(data[i::P].tolist()).most_common(1)[0][0] for i in range(P))
# key 呈 17 字节周期 -> 取前 17: b"NTESCSLLVMPASSKEY"
07 · 风险汇总、攻击面与加固建议#
本章综合第 02–06 章的发现给出结论。风险等级:严重 / 高 / 中 / 低 / 信息。
7.1 描述性风险清单#
| # | 风险 | 等级 | 证据 | 影响 |
|---|---|---|---|---|
| R1 | global-metadata.dat 采用固定 17 字节周期 XOR(NTESCSLLVMPASSKEY)保护 |
中 | 第 6 章;两小时即可离线还原 | 全量 C# 符号泄露,IL2CPP 逆向门槛大幅降低 |
| R2 | 玩法逻辑为加密 Lua 字节码,且解密方法名(DecryptLuaBytecode)与密钥派生(CalcEncryptKeyFromString)均暴露 |
高 | luapackage_32/64_*.unity3d;CenturyGame.GameFramework.Helper.* |
客户端逻辑与数值可被篡改,易催生作弊/私服/破解 |
| R3 | 资源包仅用 XOR/偏移加密 + MD5 清单 | 高 | EncryptXOR/1/2、BundleEncryptOffset、GetManifestMd5 |
资源可被替换/解包,皮肤与地图等资产泄露 |
| R4 | 通知扩展 NSAllowsArbitraryLoads=true |
高 | PlugIns/.../Info.plist |
扩展内任意 HTTP 明文流量,可被中间人劫持/注入 |
| R5 | 主 App 未声明 NSAppTransportSecurity、未见证书固定 |
中 | Info.plist 无 ATS 键;二进制无 SPKI pin 实现 |
依赖系统默认 ATS;对企业代理/证书场景缺乏二次防护 |
| R6 | 生产包残留构建/发布脚本,含内网 Bitbucket 仓库与私有 Pod 源 oauth2: 引用 |
高 | install/autoUpdateRepoPublic.sh、CenturyGameResources.bundle/merge.sh |
供应链信息泄露,暴露内部命名/仓库/CI 结构,可能被用于投毒或社工 |
| R7 | 大量可逆编码的密钥/配置硬编码于 ddCommon.json |
中 | secret*、tga、rum、adjust、constants、unity_config |
密钥可逆 → 可伪造/解密对应 SDK 通道数据 |
| R8 | 归因/广告/追踪 SDK 覆盖面大且声明与行为不一致 | 中 | AdjustSdk/UnionOpenPlatformCore 隐私清单 NSPrivacyTracking=true,主清单 false |
合规(审核/法律)风险 |
| R9 | 客户端本地风控(网易易盾 NetSec:越狱/调试/模拟器/完整性检测)易被绕过 | 中 | NetEase.NetSecProtect、AntiCheatResult、NetSecGetEmulatorName、isJailbreak、SC_DATA_TAMPERED |
检测逻辑在客户端,绕过成本低,风控有效性有限 |
| R10 | 管理面域名(GM API)出现在客户端 | 中 | gofcn-gm-api-prod.campfiregames.cn 等 |
扩大攻击面,若鉴权薄弱可被探测 |
| R11 | 存在 IFix 热更新 与 远程调试运行时 | 中 | IFix.Core.dll(ilpatch/{0}.patch.bytes)、Debugger.dll、HdgRemoteDebugRuntime |
动态补丁/调试通道若在生产可用,可被滥用篡改逻辑 |
| R12 | NSLocationWhenInUseUsageDescription = "empty" 等文案瑕疵 |
低 | Info.plist |
审核风险/用户体验 |
| R13 | NSAdvertisingAttributionReportEndpoint 尾部含零宽空格 U+200B |
低/信息 | 已复现 'https://adjust-skadnetwork.com\u200b' |
可能为拷贝失误,属质量缺陷 |
| R14 | mopub-sdk-id.json 含 154 条 SKAdNetwork ID 未合并进主 Info.plist |
低/信息 | 第 2 章 | 归因测量可能不完整 |
| R15 | UnityFramework 内硬编码大量第三方 App 的 Bundle ID / 名称清单(微信、抖音、淘宝、竞品等) | 中 | 二进制字符串中约 200+ 条 com.tencent.xin/com.ss.iphone.ugc.Aweme/com.tencent.mqq 等 |
疑似用于已安装应用探测/归因/竞品识别,涉及隐私与合规 |
| R16 | 客户端内置完整 GM/作弊指令面(req_cheat_* 约 1,500+ 标识符) |
高 | req_cheat_add/recharge/query_database/change_sys_time/set_hero_level/finish_chapter/...、notify_cheat_record_info |
若服务端未严格鉴权/白名单,可加道具/改数值/改系统时间/热更,属最高危越权面 |
| R17 | 网络协议 schema 与全量配置以明文存于客户端(Lua 字节码常量) | 中 | request { owner 0 : integer } 等 2,635 条 op 定义 |
大幅降低协议逆向/伪造成本,利于中间人、重放与自动化脚本 |
7.2 攻击面总结#
┌──────────────────────────────────────────────┐
静态逆向 ─────────► │ ① global-metadata XOR → 全量 C# 符号 │
│ ② AssetBundle XOR → 资源/贴图/配置 │
│ ③ Lua 字节码加密 → 玩法逻辑 │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
运行时攻击 ───────► │ ④ 明文 HTTP(扩展) → MITM 注入 │
│ ⑤ 本地风控绕过 → 越狱/调试/内存修改 │
│ ⑥ 热更新/调试通道 → 动态代码加载 │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
供应链 ───────────► │ ⑦ 残留构建脚本/内网仓库 → 情报泄露 │
└──────────────────────────────────────────────┘
7.3 加固建议#
A. 数据与逻辑保护
1. IL2CPP 元数据改用强加密 + 完整性方案:如运行时密钥派生(结合设备/签名指纹)的 AES-GCM/ChaCha20,并校验 MAC;避免固定、可打印、可自相关的周期密钥。
2. Lua 字节码:自定义 VM/指令集 + 逐包密钥,密钥不落盘(由服务端下发或 KDF 派生),杜绝 CalcEncryptKeyFromString 这类静态可推导方式。
3. AssetBundle:在 XOR 之上叠加逐资源密钥 + 签名校验,并将关键数值/配置改为服务端权威。
B. 传输安全
4. 移除扩展的 NSAllowsArbitraryLoads,全站 HTTPS 并在主 App 明确声明 ATS 例外(如确需)。
5. 引入 证书/公钥固定(SSL Pinning) 或 mTLS,并对关键业务(登录、支付、下发配置)做端到端签名。
C. 风控 6. 将关键风控判断上移到服务端(行为序列、协议校验、回包签名),客户端检测仅作辅助。 7. 对客户端完整性(代码段/资源/Lua 包)做签名校验而非仅 MD5。
D. 供应链与合规
8. 从发布包中移除所有构建脚本、Podfile 片段、内网仓库引用与 OAuth token;发布流水线做产物白名单。
9. 统一 SDK 隐私清单与实际行为(NSPrivacyTracking),修正文案与 URL 转义(去除零宽字符),合并 SKAdNetwork 清单。
10. 管理面(GM/DevCenter)与客户端网络隔离,限制域名/接口可见性并强化鉴权与审计。
7.4 结论#
该样本的客户端加固属于"混淆型"而非"密码学型":核心保护(元数据、Lua、资源)均为可离线还原的可逆变换,其设计目标是提高自动化逆向的门槛,而非提供真正的机密性。配合未固定的传输信道、客户端本地风控与发布包内的供应链残留,整体呈现中高风险画像。
对于防守方,优先级排序为:R6(供应链泄露)> R2/R3(逻辑/资源可篡改)> R4(明文 HTTP)> R1/R7(元数据与配置可逆)> R8–R11(合规与风控)。
08 · 内购 / 充值系统(IAP)逆向分析#
目标样本:com.gof.china_1.15.6(无尽冬日 / Whiteout Survival 国服,Century Games)
引擎:Unity 2022.3.52f1 + IL2CPP + LuaJIT(XXLua 脚本层)
分析性质:授权安全研究 / 移动应用安全评估
证据来源(均只读):out_types.txt、out_identifiers.txt、out_literals.txt、lua/tokens.txt、lua/config_tables.txt、lua/ui_views.txt、lua/protocol_ops.txt、lua/lua_strings_32.txt、extracted/Payload/whiteoutsurvivalcn.app/ddCommon.json
取证说明(重要):lua_strings_32.txt 与 protocol_ops.txt 是从 Lua 字节码中按固定窗口抽取的常量/标识符,存在两类已知伪影:
① 标识符被左截断(如 notify_recharge_init 抽成 tify_recharge_init);
② 相邻字符串粘连。
因此本报告对每条 opcode 均以 lua_strings_32.txt 中带中文注释的原始记录为准,截断处如实标注为 …,不做补全臆测。凡是无法确证完整名称的,只给 opcode。
8.0 结论摘要(TL;DR)#
| 维度 | 结论 |
|---|---|
| 支付 SDK | 自研 Unity.dd.sdk.scripts.payment.dll(Century Games DD SDK 8.0.5.0-GOFCN,cg 命名空间),仅 12 个类型,全部为原生插件薄包装 |
| 结算渠道 | Apple IAP、Google IAB、Adyen、Amazon IAP、CN Media(国内媒体/渠道支付)、小游戏、官网 WebShop;CGPayment 是统一门面 |
| StoreKit | 未出现在 IL2CPP 托管元数据中(无 SKPayment/SKProduct/StoreKit 符号);SK/logic 位于原生插件,C# 侧仅通过 com_sdk_payment_appleiap_* 桥接 |
| 商品 ID | 客户端未见任何硬编码 SKU/ProductId 字符串;配置层只用整数 payment_id / pay_id 索引,实际 productId 由 服务端下发(Lua PayRead:GetProductIds) |
| 收据校验 | 由服务端承担(CGPaymentError.ErrorNotMatchUid / ErrorOriginalResponse / ErrorProviderOrderId、paymentServerApi、com_sdk_payment_getThroughCargo) |
| 充值模块族 | Lua 侧至少 20+ 个内购模块族(首充、活跃/每日、累计、累充抽奖、充值抽奖、连续、五日、月卡、月度、基金、充值商店、代币/代金券、多选礼包、可选礼包、活动礼包、订阅礼包、每日特惠、无限宝箱、宠物充值、尊享 VIP 累充、返利、网页商城等) |
| 协议面 | 充值核心 op 段 6701–6708 / 6791;活动充值 11401–11427;连续充值 9951–9983;累充抽奖 20281–20292;月卡 9401 / 9451 / 9452;充值商店 20601 |
| 总体风险 | 中:客户端不下发可信价格/商品,主信任落在服务端;但协议与配置全明文、订单回执字段外露、req_cheat_* 含充值类作弊接口,仍存在篡改/重放/私服面 |
8.1 支付 SDK 面(Unity.dd.sdk.scripts.payment)#
8.1.1 程序集类型清单
在 out_types.txt 中该 DLL 仅导出 12 个类型(行 ##### Unity.dd.sdk.scripts.payment.dll (12 types)):
<Module>
centurygame.CGCnMediaOrderParam
IDelegate
<>c__DisplayClass50_0
centurygame.CGPayment
centurygame.CGUnFetchedProduct
centurygame.CGProduct
centurygame.CGSubscriptProduct
ResultHandler
centurygame.IOS.CGAppleIapIOS
centurygame.Abstract.BasePaymentWrapper
<PrivateImplementationDetails>
关联类型(定义在 CenturyGame.GameFramework.dll 区域,由 IL2CPP 生成的 ifix 包装):
centurygame.CGPaymentError、CGPaymentDelegate、CGPaymentDelegateWrap、centurygame_CGPaymentWrap、centurygame_CGProductWrap、centurygame_CGSubscriptProductWrap、SDKEvent_SDKListProductEventWrap、SDKEvent_SDKListSubscriptProductEventWrap。
8.1.2 CGPayment 主要方法(init / pay / restore / query)
以下标识符来自 out_identifiers.txt(centurygame_CGPaymentWrap 及内部包装区域):
| 分类 | 标识符 | 说明 |
|---|---|---|
| 初始化 | SetDelegate(com_sdk_payment_appleiap_setDelegate)、StartHelper、Trying to start payment helper.(字面量) |
初始化支付助手 |
| 初始化回调 | OnInitializeSuccess / OnInitializeError;Lua 事件 SDK_PAY_INIT_SUCCESS / SDK_PAY_INIT_ERROR |
初始化结果 |
| 购买 | Buy、BuyWithServerId、com_sdk_payment_appleiap_buy、com_sdk_payment_appleiap_buy_with_serverId、com_sdk_payment_server_buy |
下单/支付;带 serverId 变体用于关单/归属 |
| 商品查询 | RequestProductDetailsWithCurrency、ObtainProductDetailsWithCurrency、GetSupportCurrencyList、GetAdyenSupportCurrencyList、RequestProductDetailsWithCurrency |
按币种拉取商品 |
| 未获取商品 | OnUnfetchedProductResponseCallback、CGUnFetchedProduct、OnFetchStoreCountryCodeComplete / FetchStoreCountryCode |
拉取失败/商店国家码 |
| 恢复/补单 | checkOngoingSubscriptOrders / getOngoingSubscriptOrders、TryCompleteUnPayOrder、CompleteUnFinishPay(com_sdk_payment_completeunfinishpay)、SetThroughCargoForOngoingPurchases、get_NonPreOrderPurchasesCallback |
恢复订阅、补发未完成订单(掉单补偿) |
| 穿透/直购 | RequestGetThroughCargoWithProviderOrderId / GetThroughCargoWithProviderOrderId(com_sdk_payment_getThroughCargo) |
以第三方订单号穿透发奖 |
| 国内媒体支付 | SetAndroidCnMediaPayOrderParam、centurygame.CGCnMediaOrderParam |
安卓国内媒体渠道参数 |
| Offer Code | EnableOfferCodeOrderHandling、IsInAppOfferCodeRedemptionSupported、PresentOfferCodeRedemptionUI、SetOfferCodeThroughCargoForOngoingPurchases |
Apple Offer Code 兑换码 |
| 非消耗品 | SetNonConsumptionProductIds / setNonConsumptionProductIds、CanMakePurchases |
非消耗型商品与可购状态 |
| 服务端接口字段 | paymentServerApi、PaymentServerApi、purchaseDataJson |
指向服务端校验 API 的配置 |
CGPayment 内部原生桥方法名(out_identifiers.txt 连续区块,最直接的渠道证据):
CGAppleIapIOS
PaymentServerApi
getOngoingSubscriptOrders
setNonConsumptionProductIds
CompleteUnFinishPay
GetAdyenSupportCurrencyList
ObtainProductDetailsWithCurrency
GetThroughCargoWithProviderOrderId
com_sdk_payment_appleiap_setDelegate
com_sdk_payment_appleiap_canMakePurchases
com_sdk_payment_appleiap_startHelperWithProductIDs
com_sdk_payment_appleiap_buy
com_sdk_payment_appleiap_buy_with_serverId
com_sdk_payment_server_buy
com_sdk_payment_getOngoingSubscriptOrders
com_sdk_payment_completeunfinishpay
com_sdk_payment_getThroughCargo
com_sdk_payment_fetchStoreCountryCode
com_sdk_payment_enableOfferCodeOrderHandling
com_sdk_payment_isInAppOfferCodeRedemptionSupported
com_sdk_payment_presentOfferCodeRedemptionUI
com_sdk_payment_setOfferCodeThroughCargoForOngoingPurchases
BasePaymentWrapper
8.1.3 回调委托 CGPaymentDelegate
out_identifiers.txt 中 CGPaymentDelegate 的成员(含 *Event 的 UnityEvent 版本):
| 回调 | 语义 |
|---|---|
OnInitializeSuccess / OnInitializeError |
支付初始化 |
OnPurchaseSuccess / OnPurchaseSuccessWithOrderId / OnPurchaseError |
购买成功/带订单号成功/失败 |
OnCheckOngoingSubscriptOrders / OnCheckOngoingSubscriptOrdersError |
订阅检查 |
OnCreateOrderIdComplete |
订单号创建完成 |
OnGetThroughCargoWithProviderIdSuccess / ...Error |
穿透发货 |
OnFetchAppleStoreFrontCountryCodeComplete |
苹果商店国家码 |
OnCommonCallBack |
通用回调 |
字段:products、productId、throughCargo、orderId、countryCode。
8.1.4 商品对象 CGProduct / CGSubscriptProduct
centurygame_CGProductWrap 暴露:
| 成员 | 说明 |
|---|---|
ProductId / GetProductId |
商品 ID(运行时从商店/服务端获得) |
Title / Description |
标题/描述 |
PriceCurrencyCode / GetPriceCurrencyCode |
币种代码 |
FormattedPrice / GetFormattedPrice |
本地化价格字符串 |
Price / GetPrice |
原始价格 |
Icon / currencyLable |
图标与币种标签 |
FromGoogleIabMessage |
Google IAB 来源解析 |
FromAdyenPayMessage |
Adyen 来源解析 |
FromAmazonIapMessage |
Amazon IAP 来源解析 |
FromAppleIapMessage |
Apple IAP 来源解析 |
FromMiniGameMessage |
小游戏来源解析 |
CGSubscriptProduct 额外含 <productId>k__BackingField、<createTs>k__BackingField(订阅创建时间)。
结论:CGPayment 是一个多渠道聚合门面,日常维护面覆盖 Apple / Google / Adyen / Amazon / 小游戏 / 国内媒体(CN Media)。Android 国服主渠道 = CN Media(SetAndroidCnMediaPayOrderParam + CGCnMediaOrderParam)。
8.1.5 支付错误对象 CGPaymentError
centurygame_CGPaymentErrorWrap 字段(out_literals.txt 同步可见 ErrorProductId / ErrorProviderOrderId / ErrorNotMatchUid / ErrorOriginalResponse、not_match_uid):
| 字段 | 含义 |
|---|---|
ErrorProductId |
出错商品 ID |
ErrorProviderOrderId |
第三方(渠道)订单号 |
ErrorNotMatchUid |
订单归属 UID 不匹配(关键的防串单/防重放字段,对应字面量 not_match_uid) |
ErrorOriginalResponse |
渠道原始回执(收据原文) |
ErrorThroughCargo |
穿透发货标记 |
以及 LogPayment / LogPayment productId is null 日志点。
8.1.6 StoreKit 证据
out_types.txt、out_identifiers.txt、out_literals.txt中 检索不到StoreKit、SKPayment、SKProduct、SKMutablePayment、SKPaymentQueue等符号。- 只有
centurygame.IOS.CGAppleIapIOS与com_sdk_payment_appleiap_*桥接名,以及日志CGAppleIapIOSCommon called. iosDelegate =/CGAppleIapIOSCommon failed: {0}.。
结论:iOS 的 StoreKit 调用发生在原生插件内(CGAppleIapIOSCommon),IL2CPP 托管层只做薄包装,因此 StoreKit 逻辑不在 global-metadata.dat 可达范围内,需转向原生二进制分析。
8.1.7 Lua 侧 SDK 桥与事件
| 标识符 | 来源 | 说明 |
|---|---|---|
SDKHelper |
lua_strings_32.txt(SDKHelper、SDKHelper Buy、SDKHelper Init 等) |
Lua 调用 C# SDK 的统一入口 |
SDK_PAY_INIT_SUCCESS / SDK_PAY_INIT_ERROR |
lua_strings_32.txt |
支付初始化事件 |
SDK_PAY_BUY_SUCCESS / SDK_PAY_BUY_ERROR |
lua_strings_32.txt |
支付购买事件 |
OnPayInitSuccess / OnPayInitError / OnPayBuySuccess / OnPayBuyError |
lua_strings_32.txt |
对应处理函数 |
OnPaymentInitializeSuccess / OnPaymentInitializeError / OnPaymentPurchaseSuccess / OnPaymentPurchaseError / OnPaymentPurchaseSuccessWithOrderId |
out_literals.txt |
C# 回调名 |
CGWebShop / centurygame.CGWebShop |
lua_strings_32.txt、out_identifiers.txt |
官网网页商城 |
SDK_WEB_SHOP_QRCODE_SCAN_RESULT、SDK_WEB_SHOP_USER_AUTHORIZATION_RESULT、SDK_WEB_SHOP_USER_CANCEL_AUTHORIZATION_RESULT |
lua_strings_32.txt |
WebShop 授权/扫码事件 |
OnAppWebShopUserAuthorizationRequestReceived、ParseSDKPayloadJson |
lua_strings_32.txt |
授权结果解析 |
Lua 侧还出现 pay.PayCtrl、PayCtrl、PayRead、GiftCtrl、GiftRead、payment_config、TraceData_Pay 等业务层符号。
8.1.8 支付错误码(Lua 内置,CGError*)
从 lua_strings_32.txt 完整抽取(这是客户端支付错误文案/码的权威集合):
| 错误码 | 标识符 | 语义 |
|---|---|---|
| 3000 | CGErrorFailedToConnectToPaymentServer_3000 |
无法连接支付服务器 |
| 3001 | CGErrorInvalidPaymentRequestData_3001 |
支付请求数据非法 |
| 3002 | CGErrorFailedToParsePaymentResponse_3002 |
支付响应解析失败 |
| 3003 | CGErrorPaymentResponseError_3003 |
支付响应错误 |
| 3004 | CGErrorPaymentRequestError_3004 |
支付请求错误 |
| 3005 | CGErrorAppleVerificationFailed_3005 |
苹果凭据校验失败 |
| 3006 | CGErrorAppleVerificationTimeout_3006 |
苹果凭据校验超时 |
| 3007 | CGErrorPaymentUninitialized_3007 |
支付未初始化 |
| 3008 | CGErrorPaymentUserNotLogged_3008 |
用户未登录 |
| 3009 | CGErrorPaymentPurchasing_3009 |
支付进行中 |
| 3200 | CGErrorPurchaseFailed_3200 |
购买失败 |
| 3201 | CGErrorProductsInfoInvalid_3201 |
商品信息非法 |
| 3202 | CGErrorBuyProductIdInvalid_3202 |
无效的商品 |
| 3203 | CGErrorNoLocalReceipt_3203 |
无本地收据 |
| 3204 | CGErrorBuyCurrencyCodeInvalid_3204 |
币种代码非法 |
| 3205 | CGErrorProductsIsError_3205 |
商品信息错误 |
| 3206 | CGErrorProductsIsEmpty_3206 |
商品为空 |
| 3207 | CGErrorPaymentCanceled_3207 |
支付取消 |
| 3208 | CGErrorPaymentInvalid_3208 |
支付非法 |
| 3209 | CGErrorPayment…NotAllowed_3209 |
用户不允许授权付款 |
| 3210 | CGErrorStoreProductNotAvailable_3210 |
商店商品不可用 |
| 3211 | CGErrorCloudServicePermissionDenied_3211 |
云服务权限拒绝 |
| 3212 | CGErrorCloudServiceNetworkConnectionFailed_3212 |
云服务网络失败 |
| 3213 | CGErrorCloudServiceRevoked_3213 |
云服务撤销 |
| 3220 | CGErrorClientInvalid_3220 |
客户端非法 |
| — | err_payment_initialize_de… |
“支付初始化失败,请尝试重新登录或者联系客服” |
| — | recharge_ui_022 |
“商品购买失败,请联系客服解决!” |
| — | web_shop_err_1207 / 1201 / 121 / 2233 / 2234 / 2235 |
网页商城错误码 |
备注:CGErrorPayment…NotAllowed_3209 在抽取中出现左截断,但同源中可见完整文案“用户不允许授权付款”,码值 3209 可靠。
8.2 充值配置与模块(Lua 侧)#
8.2.1 内购相关配置表(config_tables.txt / game_config_default_*)
config_tables.txt 收录 1,599 个表名(抽取窗口 ~40 字符,长名被截断)。内购/礼包/商店相关表如下(原样引用):
| 表名(原样) | 归属模块 |
|---|---|
game_config_default_recharge_shop_c…、game_config_default_recharge_shop_i… |
充值商店(conf / item) |
game_config_default_recharge_lo…、…recharge_lottery_c…、…recharge_lottery_guarant…、…recharge_lottery_rew…、…recharge_lottery_s… |
充值抽奖 |
game_config_default_active_recharge_acti…、…active_recharge_activity_t…、…active_recharge_coll… |
活跃/每日充值 |
game_config_default_month_recharge_c… |
月度充值 |
game_config_default_month_integral_battle_p…、…month_integral_bp_rew… |
月度积分战令 |
game_config_default_pet__recharge_c…、…pet_recharge_conf… |
宠物充值 |
game_config_default_payment_frost_s… |
支付(霜冻主题) |
game_config_default_gift_activity_…、…gift_activity_gr…、…gift_activity_mod… |
活动礼包 |
game_config_default_activity_gift_po… |
活动礼包弹窗 |
game_config_default_optional_gift_p…、…optional_gift_pack_cont… |
可选礼包 |
game_config_default_multiple_choice_p…、…multiple_choice_pack…、…multiple_choice_pack_cont…、…push_multiple_choice_* |
多选礼包(含推送) |
game_config_default_subscription_gift_con…、…subscription_gift_confi… |
订阅礼包 |
game_config_default_daily_specials_g…、…daily_specials_gift…、…daily_specials_gr…、…daily_specials_hero_g…、…daily_specials_paym… |
每日特惠 |
game_config_default_personal_gift_gr… |
个人礼包 |
game_config_default_mobilization_gift_con…、…mobilization_gift_config… |
动员礼包 |
game_config_default_birthday_gift_pre…、…sup_vip_birthday_g… |
生日礼包 |
game_config_default_stove_skin_gift_c…、…stove_skin_gift_cli… |
炉灶皮肤礼包 |
game_config_default_three_tiles_gift… |
三消礼包 |
game_config_default_threedays_shop_c…/g…/i…/rew…/st… |
三日商店 |
game_config_default_store_item_buy_li…、…store_item_sp_c…、…store_quick_buy_con…/confi… |
商店通用购买 |
game_config_default_garden_token_shop_c…、…lord_skin_shop_c…、…lordskin_shop_cli…、…puzzle_shop_item… |
各类主题商店 |
game_config_default_midasbuy_gift_cont… |
MidasBuy 商城礼包 |
game_config_default_red_packet_config…、…truck_escort_diamond_c…、…vip_extra_rewa…、…sup_vip_info_con… |
红包/钻石/VIP |
game_config_default_fes_battlepass_*、…battlepass…、…bp_* |
战令(付费通道) |
lua_strings_32.txt 直接出现 payment_config(支付配置表,运行时由 GConfig 读取)。
8.2.2 内购模块族枚举(≥20 族)
下表由 ui_views.txt(6,976 项)+ lua_strings_32.txt + tokens.txt 交叉确认(模块路径前缀亦可见于 ui_views.txt,如 game_module_ui_item_firstrecharge_FirstRechargePanel、game_module_logic_module_continuousrecharge_ContinuousRechargeCtrl、game_module_logic_module_welfare_MonthCardCtrl):
| # | 模块族 | 代表符号(证据) |
|---|---|---|
| 1 | 基础充值 / 支付 | RechargeCtrl、PayCtrl、PayRead、GiftCtrl、GiftRead、payment_config、pay.PayCtrl |
| 2 | 首充 | FirstRechargePanel、FirstRechargePopPanel/PopView、FirstRechargeHelper、first_recharge、GameConfig 字段 first_recharge_award、first_recharge_ts |
| 3 | 活跃/每日充值 | ActiveRechargeCtrl/Panel/Item、active_recharge_init、ACTIVE_RECHARGE_INIT |
| 4 | 累计充值 | AccumulativeRechargePanel、AccumulativeRechargeView、AccumulativeRechargePopupView、AccumulativeRechargeTierClaim、Accumulative_Recharge_Main_View |
| 5 | 累充抽奖 | AccRechargeLotteryCtrl、AccRechargeLotterySubView、AccRechargeLotteryTipView、AccRechargeLotteryGetMorePopupView、AccRecharge_Lottery |
| 6 | 充值抽奖 | RechargeMainView、recharge_lottery_*(配置)、REQ_RECHARGE_LOTTERY_DRAW_SUCCESS |
| 7 | 连续充值 | ContinuousRechargeCtrl/Panel、ContinuousRechargeValentinesDayPanel、continuous_recharge_info |
| 8 | 五日充值 | FiveDaysRechargeCtrl、FiveDaysRechargeMainSubView、FiveDaysRechargeRewardChoosePanel、FiveDaysRechargeGiftItem |
| 9 | 月卡 | MonthCardCtrl、MonthCardPanel、MonthCardRewardPopupView、WelfareMonthCardPanel、MonthCardDefine/Read、month_card |
| 10 | 月度充值 | month_recharge 配置、month_integral_bp_* |
| 11 | 充值基金 | FundCtrl、SnowFundCtrl、WelfareFundPanel、fx_recharge_fund_diamond_jxm、charge_fund_equip |
| 12 | 充值商店 | RechargeShopCtrl、RechargeShopSubPanel、RechargeShopItem、RechargeShopHelper/Define、recharge_shop_conf、recharge_shop_item |
| 13 | 代币/代金券充值 | GiftRechargeTokenPanel、IsShowRechargeTokenPanel、voucher_id、pay_id/pay_type/num/name/tokenPrice |
| 14 | 多选礼包 | MultipleChoicePackCtrl/Panel/PopupView、PushMultipleChoicePackCtrl/Panel |
| 15 | 可选礼包 | OptionalGiftCtrl/Panel、optional_gift_pack_*、OptionalGiftItem、gift_subgroup |
| 16 | 活动礼包 | ActivityGiftCtrl、ActivityGiftLinkageMonopolySlapFacePopupView、gift_activity_* |
| 17 | 订阅礼包 | subscription_gift_con/confi |
| 18 | 每日特惠 | daily_specials_*(含 daily_specials_payment) |
| 19 | 无限/无双宝箱 | InfiniteBoxCtrl、InfiniteBoxPopupItem、InfiniteBoxPopUpView、GetInfiniteBoxPayCfg |
| 20 | 尊享 VIP 累充 | svip_recharge_reward_info、SupVipBirthday、get_recharge_reward |
| 21 | 返利/回馈 | PaybackCtrl/PaybackView、OpenPaybackView、Payback_Gift |
| 22 | 网页商城 | CGWebShop、SettingsWebShopLoginAuthorizationView、OpenAppWebShopLoginAuthorizationView |
| 23 | 通用礼包系统 | GiftPanel、GiftSubPanel、GiftBagSinglePanel、GiftTrigger*、MoreGiftView、CommonGetMoreGiftView |
计数:ui_views.txt 中含 Recharge 37 项、Gift 157 项、Shop 20 项、Store 84 项、MonthCard 9 项;tokens.txt 中 Recharge 相关 937 条、Gift 2,804 条。模块族按 母功能 归纳为 23 族(含通用礼包系统)。
8.2.3 关键数据结构(协议 schema,含中文注释)
从 lua_strings_32.txt 抽取到的充值/礼包相关字段定义(原样):
payment_id 4 : integer
ori_price 11 : integer
cur_order 4 : integer
total_recharge_diamond 4 : integer # 累计充值钻石
total_recharge_times 3 : integer
first_recharge_award 2 : *boolean # 首充奖励情况
first_recharge_ts 2 : integer # 首充时间
total_recharge_amount 0 : integer(0) # 累计充值金额(美[元])
recharge_amount ? : integer # 首充金额(nil 设为非首…)
buy_gift_mark 6 : *mark() # 购买标记, key=or…
conf_map 0 : *gift_config
card_info 0 : month_card # 月卡数据
cardList 0 : *month_card # 月卡列表
data 0 : recharge_data
gift_order ? : …
recharge_idx ? : …
ori_diamonds_price ? : …
index 1 : integer # 档位,对应配置表 index
充值商店配置注释:index 1: integer # 物品组在 recharge_shop_item 表中的下标;cost_currency(recharge_shop_conf 的消耗货币字段)。
代币/代金券礼包配置(pay_id 维度):Parent _giftPrice _giftPayId TxtTokenPrice GetResIcon ImgToken pay_id pay_type num name。
8.3 服务端 / 协议流程#
8.3.1 相关 opcode(来自 protocol_ops.txt + lua_strings_32.txt 中文注释)
下列 opcode 与名称已在 lua_strings_32.txt 的带注释记录中交叉验证;被左截断的记录以 … 标注。
| opcode | 名称 | 中文注释/语义 |
|---|---|---|
| 6701 | notify_recharge_init |
充值系统初始化/同步 |
| 6702 | notify_buy_diamond_success |
购买钻石成功 |
| 6704 | notify_recharge_fail |
充值失败 |
| 6705 | req_recharge_get_first_reward |
领取首充 |
| 6706 | …ative_reward(截断) |
领取累充奖励 |
| 6707 | req_recharge_by_voucher |
代金券充值 |
| 6708 | req_recharge_by_token |
代币充值 |
| 6791 | req_beta_test_recharge |
内测充值(测试/作弊口) |
| 7405 | req_remove_gift_group |
移除礼包组 |
| 7406 | notify_gift_config(gift_config) |
通知礼包配置 |
| 7431 | req_gift_buy_free_gift |
购买免费礼包 |
| 9401 | req_month_card_get_daily_reward(_month_card_get_daily_reaward) |
领取月卡每日奖励 |
| 9451 | notify_month_card_info |
月卡数据通知 |
| 9452 | notify_month_card_active |
月卡开放或购买通知 |
| 9951 | req_continuous_recharge_daily_reward |
连续充值领取每日奖励 |
| 9981 | notify_continuous_recharge_info |
连续充值同步活动信息 |
| 9982 | notify_continuous_recharge_update(…ous_recharge_update) |
更新充值金额 |
| 2001 | req_store_quick_buy(…uick_buy) |
商店快捷购买 |
| 2003 | req_shop_buy |
商店购买 |
| 2004 | req_shop_refresh |
商店刷新 |
| 11401 | req_get_active_recharge_quest_reward |
活跃充值任务领奖 |
| 11402 | active_recharge_score_reward |
领取宝箱奖励 |
| 11426 | active_recharge_init |
同步数据 |
| 11427 | notify_update_active_recharge_quest |
活跃充值任务更新 |
| 11451 | multiple_choice_pack_get_free_box |
多选礼包免费盒 |
| 11476 | notify_multiple_choice_pack_init |
多选礼包初始化 |
| 11477 | notify_multiple_choice_pack_buy_success |
多选礼包购买成功 |
| 13629 | req_vip_customize_buy_success |
VIP 自选购买成功通知 |
| 13631 | svip_recharge_reward_info |
尊享 VIP 累充奖励 |
| 17126 | notify_snow_fund_purchase |
购买通行证(基金)通知 |
| 17602 | notify_pick_one_gift_recharge_success |
自选礼包充值成功 |
| 20281 | req_recharge_lottery_draw |
充值抽奖 |
| 20282 | req_recharge_lottery_acc_box |
领取积分进度奖励 |
| 20291 | notify_recharge_lottery_info |
抽奖活动数据 |
| 20292 | recharge_lottery_point_update(…harge_lottery_point_update) |
抽奖积分更新 |
| 20601 | req_recharge_shop_buy_item |
购买充值商店物品 |
| 20896 | notify_daily_claim_pay_success |
购买特权成功 |
| 21131 | req_activity_gift_popup_claim |
活动礼包弹窗领取 |
其他相关:notify_recharge_new_token_success、notify_recharge_shop_update_integral、REQ_FIVEDAYS_RECHARGE_*、notify_fes_bp_purchase(11683)、notify_fes_bp_dailytask_purchase(13782)、notify_fishing_bp_purchase(13482)、notify_back_bp_purchase(15212)、notify_frozen_soil_purchase(13232)、notify_lost_treasure_purchase(12912)、_gift_by_diamond(12301, 钻石购买礼包)、req_super_vip_get_free_reward(13603)、req_vip_birthday_reward_push(13630)。
8.3.2 客户端 → 服务端 购买/校验流程(推断)
综合 CGPayment 回调链、Lua 事件与协议面,典型流程为:
[客户端]
1. 玩家点击购买(PayCtrl:Buy / OnClickBuy)
2. 客户端仅持有整数 payment_id / pay_id(来自 payment_config / 礼包配置)
3. 向服务端请求“下单/取商品” ──► op 6701 notify_recharge_init / 充值初始化
(PayRead:GetProductIds 取得 productId 列表)
4. 服务端返回 productId + ServerId/orderId,客户端调 SDK:
CGPayment.Buy(productId) / BuyWithServerId(productId, serverId)
└─ iOS: com_sdk_payment_appleiap_buy[_with_serverId](原生 StoreKit)
└─ Android 国服: SetAndroidCnMediaPayOrderParam + CN Media 渠道支付
5. 渠道回调 → OnPurchaseSuccessWithOrderId(orderId) / OnPurchaseError
Lua 事件:SDK_PAY_BUY_SUCCESS / SDK_PAY_BUY_ERROR
[服务端]
6. 客户端携带 purchaseDataJson / receipt 调 paymentServerApi
└─ com_sdk_payment_getThroughCargo(以 providerOrderId 穿透发货)
7. 服务端向渠道(Apple/Adyen/…)校验收据,比对 uid
└─ 失败字段:ErrorNotMatchUid / ErrorOriginalResponse / ErrorProviderOrderId
8. 发货 → notify_buy_diamond_success(6702) / notify_recharge_fail(6704)
+ notify_total_recharge_amount / notify_month_card_active / 各活动 notify_*
[补偿]
checkOngoingSubscriptOrders / TryCompleteUnPayOrder / CompleteUnFinishPay
(订阅恢复、掉单补发)
结论:真正的发货决策在服务端(比对 uid、校验渠道回执)。客户端的 CGPayment 仅负责“唤起渠道收银台 + 回传凭据”,符合业界做法,但也意味着所有信任锚点在服务端的收据校验实现质量上。
8.4 商品 / SKU / 价格证据#
8.4.1 是否存在硬编码 Product ID?
out_literals.txt/out_identifiers.txt中没有任何com.gof.china.*形式的商品 SKU;com.gof.china只作为GlobalDefine的包名出现(与OpenAppStore、SDKHelper同区)。- 唯一与商品相关的字符串是运行时字段与错误:
productId、product_id、ProductId、`productIdmust not be empty.、LogPayment productId is null`。 - 配置层以整数
payment_id(payment_id 4 : integer)与pay_id/pay_type索引,而非字符串 SKU。
明确结论:商品 ID(ProductId/SKU)由服务端下发(Lua PayRead:GetProductIds;CGPayment.paymentServerApi 指向服务端支付 API)。客户端不内置可信商品表,属正向设计。
8.4.2 价格 / 档位方案
| 证据 | 说明 |
|---|---|
ori_price 11 : integer |
礼包配置的原价(整数,非浮点,疑似最小货币单位/角分) |
ori_diamonds_price |
钻石礼包原价(与 reward_group、gift_order 同区,见 activities_infinite_box_* 配置常量) |
tokenPrice / _giftPrice / TxtTokenPrice |
代币/代金券礼包价格 |
discount / TxtPrice / TxtOriginalPrice |
折扣与现价展示 |
total_recharge_amount 0 : integer(0) # 累计充值金额(美…) |
累计充值金额(注释指向“美元”,单位疑为分) |
recharge_amount # 首充金额(nil 设为非首…) |
首充金额字段 |
index 1 : integer # 档位,对应配置表 index、id 0 : integer # 奖励档位 |
“档位”以配置表 index/奖励组表达 |
0%、30%、50%、70%、90%五个档位 |
某活动档位示例(一档多奖励) |
# 已购买到的档位序号 |
累充档位购买状态 |
1265 # 设置首充金额账号(req_cheat_set_bind_account_1265) |
服务端/作弊口可设置首充金额 |
首充/月卡中文文案证据:首充(10 处)、月卡(16 处)、礼包(285 处)、档位(40 处)、充值(76 处)、代金券(3 处)、钻石(162 处)、订单(9 处)、支付(24 处)。例如:
- 首充:_name}</color>通过首充豪礼,获得传说级英雄…、完成首充后开启、首充奖励
- 月卡:已购买的周卡/月卡奖励、超值月卡优惠券(%{num1})、情报月卡-充值奖励、月卡奖励可在…
- 代金券:req_recharge_by_voucher 6707 { # 代金券充值、…档位的礼包时,可消耗该代金券进行全额抵扣。
8.4.3 币种 / 本地化价格
GetSupportCurrencyList/GetAdyenSupportCurrencyList(CGPayment)GetPriceByCurrencyCode、GetPriceCurrencyCode、FormattedPrice(CGProduct)- 字面量
GetPriceByCurrencyCode->Error:{0}、Currency2CultureDic、Currency2CultureListDic、CurrencyHelper、InitCurrencyCodeToCulture
即:多币种由 SDK 向渠道查询(RequestProductDetailsWithCurrency),客户端用 FormattedPrice 展示;因此本地展示价不等于服务端发货依据。
8.4.4 渠道/账号类型(FPAccountType*)
out_identifiers.txt 中支付相关账号类型:FPAccountTypeApple、FPAccountTypeAlipay、FPAccountTypeCnMedia、CGAccountTypeUtils、AuthorizeVipMallUserBean。
ddCommon.json(应用级配置,部分值为可逆编码/加密 blob):
| 键 | 值 |
|---|---|
platform.android.android_package_name |
com.gof.china |
platform.*.game_id |
20173 |
platform.*.china |
True |
platform.android.wechat_app_id |
wx32374c9038cf9759 |
platform.ios.douyin_client_key |
awebivvetg90lv93 |
platform.android.bugly_id / platform.ios.bugly_id |
1ea45351f9 / 341c16d88c |
adjust / secret* / tga / rum / umbra_gateway / unity_config |
均为 16 进制 blob(可逆编码/加密,详见第 02/05 章) |
App Store 链接(out_literals.txt):apps.apple.com/cn/app/…/id6478492012;支付文档残留:官网链接:https://docs.open.alipay.com/。
8.5 商店 / 礼包 UI(ui_views.txt)#
| 功能 | 关键 View / Panel / Ctrl |
|---|---|
| 主充值入口 | RechargeMainView、RechargeCtrl、RechargeShopSubPanel、CanSeeInRechargeView、GiftRechargeTokenPanel |
| 首充 | FirstRechargePanel、FirstRechargePopPanel、FirstRechargePopView |
| 活跃/累计 | ActiveRechargeCtrl/Panel、AccumulativeRechargePanel、AccumulativeRechargePopupView、Accumulative_Recharge_Main_View |
| 累充抽奖 | AccRechargeLotteryCtrl、AccRechargeLotterySubView、AccRechargeLotteryTipView、AccRechargeLotteryGetMorePopupView |
| 连续/五日 | ContinuousRechargeCtrl/Panel、FiveDaysRechargeCtrl、FiveDaysRechargeMainSubView、FiveDaysRechargeRewardChoosePanel |
| 月卡 | MonthCardCtrl、MonthCardPanel、MonthCardRewardPopupView、WelfareMonthCardPanel、WelfareMonthCardDiscountPopupView |
| 基金 | FundCtrl、SnowFundCtrl、WelfareFundPanel、OpenBuyFundView |
| 商店 | StoreMainView、StoreCtrl、AllianceShopView、LordSkinStorePanel、GardenStoreMainView、PuzzleStorePopupView、CommonExchangeStoreView |
| 礼包 | GiftPanel、GiftSubPanel、GiftBagSinglePanel、MoreGiftView、OptionalGiftPanel、MultipleChoicePackPanel、PushMultipleChoicePackPanel、CommonGetMoreGiftView |
| 活动礼包 | ActivityGiftCtrl、ActivityGiftLinkageMonopolySlapFacePopupView、HeroTurntablePackageDetailView、PolarExpeditionPackageDetailView |
| VIP 相关 | VipPrivilegeStoreSubView、VipExclusiveGiftPackageSubPanel、SuperVip* |
| 网页商城 | SettingsWebShopLoginAuthorizationView、OpenAppWebShopLoginAuthorizationView、ShowStoreView、ShowPackageDetailView |
| 返利 | PaybackCtrl、PaybackView、OpenPaybackView |
| 钻石相关 | DiamondView、ActivityDiamondStorePanel、BuyItemUseLargeDiamondSurePopView、OpenDiamondBuyRewardView |
顶部模块路径证据(ui_views.txt 原样,证明模块归属):
game_module_logic_module_continuousrecharge_ContinuousRechargeCtrl
game_module_logic_module_welfare_MonthCardCtrl
game_module_ui_item_firstrecharge_FirstRechargePanel
game_module_ui_item_gift_GiftBagFishingSinglePanel
game_module_ui_view_chat_ChatGifEmojiGiftPopupView
8.6 风险分析(内购专章)#
8.6.1 风险清单
| # | 风险 | 等级 | 证据 | 影响 |
|---|---|---|---|---|
| IAP-R1 | 协议与配置全明文:充值 op、字段、错误码、礼包配置均可从 Lua 字节码还原 | 中 | 本报告 8.2–8.3;req_recharge_by_token 6708、payment_id 等 |
大幅降低伪造成本,利于自动化脚本/中间人 |
| IAP-R2 | 订单回执链路过客户端:purchaseDataJson、receipt、orderId 经客户端回传服务端 |
中高 | purchaseDataJson、OnPurchaseSuccessWithOrderId、com_sdk_payment_getThroughCargo |
若服务端校验不严(仅信任客户端字段),可伪造/重放过期收据 |
| IAP-R3 | 客户端存在充值类作弊接口 | 高 | …_cheat_recharge(1211, “礼包测试接口”)、req_beta_test_recharge(6791)、req_cheat_add_total_recharge_diamond(14169)、…_cheat_set_bind_account(1265, 设置首充金额) |
若服务端未按权限白名单拦截,可直接刷累充/首充金额,绕过真实支付 |
| IAP-R4 | 价格/商品非客户端可信(正向设计,但依赖服务端) | 中 | price/FormattedPrice 仅展示;productId 服务端下发 |
客户端改价无效;但服务端若回退信任客户端上报金额,则可低价购高价值礼包 |
| IAP-R5 | UID 串单/重放防护仅见字段名 | 中 | ErrorNotMatchUid、not_match_uid、ErrorOriginalResponse |
需在服务端验证“订单↔UID↔商品↔金额”四元绑定;仅靠字段存在不能证明已实现 |
| IAP-R6 | StoreKit 逻辑在原生插件,托管层不可见 | 信息 | CGAppleIapIOSCommon、com_sdk_payment_appleiap_*;无 SKPayment 符号 |
常规 IL2CPP 审计无法覆盖 iOS 收银台细节,需原生逆向 |
| IAP-R7 | 重打包(repack)后仍可通过 CN Media/渠道复位 | 中 | SetAndroidCnMediaPayOrderParam、FPAccountTypeCnMedia、FPAccountTypeAlipay |
第三方渠道回调的签名/来源校验若薄弱,可被中间人伪造回调 |
| IAP-R8 | 官网 WebShop 授权链路暴露 | 中 | CGWebShop、SDK_WEB_SHOP_USER_AUTHORIZATION_RESULT、AuthorizeUserWithParam/CancelAuthorizeUser |
WebShop 授权 token 若在客户端生成/校验,可被复用或劫持 |
| IAP-R9 | 多币种/跨区价格由 SDK 查询,若服务端以本地展示价结算 | 低中 | RequestProductDetailsWithCurrency、GetPriceByCurrencyCode->Error:{0} |
跨区套利/价格错配风险 |
8.6.2 攻击者视角(可做什么)
- 协议重放/伪单:抓取
purchaseDataJson/orderId,若服务端对同一providerOrderId未做幂等或uid绑定校验,可重复提交(→ 依赖ErrorNotMatchUid的实际实现)。 - 作弊接口越权:直接构造
…_cheat_recharge(1211) /req_cheat_add_total_recharge_diamond(14169) /…_cheat_set_bind_account(1265),若缺少服务端权限判定,可凭空获得累充/首充权益(最高危)。 - 假渠道回调:针对 CN Media / Alipay 回调,若未强校验渠道签名与商户号,可伪造“支付成功”。
- 补单滥用:滥用
CompleteUnFinishPay/TryCompleteUnPayOrder/checkOngoingSubscriptOrders触发重复发货。 - 改价展示欺骗:本地改
FormattedPrice仅影响 UI,无法直接获益(除非服务端信任客户端上报金额)。 - 私服:既然商品 ID 由服务端下发且协议明文,搭建私服可自定义
payment_config与发货逻辑,绕开官方计费。
8.6.3 加固建议(面向厂商)
- 收据校验全部服务端化:校验 Apple/渠道收据签名链、
providerOrderId唯一性、uid↔商品↔金额四元绑定,落实ErrorNotMatchUid语义。 req_cheat_*/req_beta_test_recharge在生产环境禁用或强制 GM 白名单+审计。- 客户端
payment_id仅作索引,服务端按 ID 查表定价;永不信任客户端上报金额。 - CN Media/Alipay 回调强制渠道签名验签 + 商户号/应用号比对 + 时间戳防重放。
- 补单接口加幂等键(
providerOrderId)与频率限制。 - WebShop 授权 token 使用服务端签发的一次性授权码,绑定 UID 与有效期。
- 对关键支付事件(6701/6702/6704/6705)做完整性校验与异常告警(数量、金额、UID 维度)。
8.7 附录:证据文件与关键符号对照#
| 类别 | 文件 | 关键符号 |
|---|---|---|
| 支付 DLL 类型 | artifacts/out_types.txt |
Unity.dd.sdk.scripts.payment.dll、centurygame.CGPayment、CGAppleIapIOS、CGCnMediaOrderParam |
| 支付方法/回调 | artifacts/out_identifiers.txt |
Buy、BuyWithServerId、CompleteUnFinishPay、com_sdk_payment_*、CGPaymentDelegate |
| 字面量/日志 | artifacts/out_literals.txt |
`productId` must not be empty.、Trying to start payment helper.、paymentDict:、GetSupportCurrencyList |
| Lua 配置表 | artifacts/lua/config_tables.txt |
game_config_default_recharge_shop_*、…gift_activity_*、…month_recharge_c* |
| Lua UI | artifacts/lua/ui_views.txt |
FirstRechargePanel、MonthCardCtrl、RechargeShopSubPanel、AccRechargeLotteryCtrl |
| Lua 协议 | artifacts/lua/protocol_ops.txt + lua_strings_32.txt |
6701..6708、9401/9451/9452、20281..20292、20601 |
| App 配置 | extracted/.../ddCommon.json |
com.gof.china、game_id=20173、wechat_app_id、secret* |
免责声明:本报告依据已脱壳样本的静态证据撰写,仅用于授权的移动应用安全评估与防护改进,不含任何可直接用于非法牟利的具体攻击载荷。所有标识符/opcode 均来自上述只读证据文件,截断处已以 … 标注,未作补全臆测。
09 · 玩法 / 系统架构分析(Gameplay / Systems Architecture)#
目标:com.gof.china 无尽冬日(Whiteout Survival 国服)1.15.6
引擎:Unity 2022.3.52f1 + IL2CPP;游戏业务逻辑位于 LuaJIT 字节码(luapackage_32/64),已还原为 Lua 文本后抽取符号
数据来源(只读证据,均位于 artifacts/lua/):
| 证据文件 | 规模 | 说明 |
|---|---|---|
tokens.txt |
252,026 行 | Lua 标识符(含 game_module_* 模块路径、方法名、视图名) |
protocol_ops.txt |
2,635 条 | 操作码<TAB>协议名(网络消息路由表) |
ui_views.txt |
6,977 个 | View / Panel / Ctrl 类名 |
config_tables.txt |
1,600 张 | 配置表名(game_config_*) |
lua_strings_32.txt |
386,532 行(~602k 可打印串) | 原始常量;内含协议 DSL 文本 |
../../artifacts/out_types.txt |
8,649 行 | IL2CPP 各程序集类型(C# 侧) |
数据质量说明:LuaJIT 字符串池抽取时存在定长截断,部分 token / 协议名首尾被截(如 req_ → eq_/q_,alliance → lliance)。因此下文所有计数均为证据内可见值,关键结论附原始条目。
1. 模块架构:game_module_logic_module_* 家族#
1.1 命名规范
一套 game_module_<层>_<系统>_<子模块>_<类名> 的路径式命名,层级为内嵌 Lua 模块树:
| 前缀 | token 数 | 含义 |
|---|---|---|
game_module_logic_module_ |
1,367 | 业务逻辑模块(Controller / Data / Model) |
game_module_logic_proxy_ |
18 | 逻辑代理(跨模块/跨服桥接) |
game_module_scene_ |
1,726 | 场景/表现层(世界地图、雪场、战斗、花园…) |
game_module_ui_ |
8,143 | UI 层(view=3,222 / item=4,892 / component=12) |
game_module_btree_ |
122 | 行为树 / 引导(action / precondition / ctrl / base) |
game_module_hfsm_state_ |
53 | 分层状态机-状态(怪物/花园/钓鱼/塔防 AI) |
game_module_hfsm_condition_ |
46 | 分层状态机-条件 |
game_module_httpdownload_ / game_module_load_ |
11 / 4 | 资源下载与加载 |
game_module_logic_module_ 下共有 196 个顶层系统家族(按第 3 段去重)。
1.2 家族规模(token 数 Top,去截断后)
| 家族 | token | 家族 | token | 家族 | token |
|---|---|---|---|---|---|
| activity | 323 | arsenalleague | 19 | hero / dress / buff | 6 each |
| world | 220 | pitchpot | 18 | kingdomcard / mail | 6 |
| alliance | 50 | chat | 17 | linkagemonopoly / soldier | 6 |
| arsenalwar | 32 | trianglewar | 15 | truck / icefieldhunter | 5 |
| overview | 31 | city | 14 | digtreasure / firecrystalscience | 5 |
| supremacywar | 30 | slg3league / pet / gift | 12 | favorites | 11 |
| minewar | 25 | player / findinggame / equip | 10 | garden | 20 |
| expert / brawlwar | 22 | thanksgiving / supremacywaractivity / slotmatrix | 8 | settings | 7 |
1.3 按系统分组(含代表类名与 moduleDefine)
| 系统域 | 家族 | 代表类(证据原文 token) |
|---|---|---|
| 城市 / 建造 | city, cityfield, citygain, homeland |
city_CityC, city_GateC, city_SurvivorC, city_SnowGuideCtrl, city_PivotFightCtrl;ConstructDef/FurnitureDef/DecorationDef/CityGainDef |
| 资源 / 背包 / 队列 | backpack, queue, store, valueexchange |
backpack_BackpackC, queue_QueueC, store_StoreC/StoreGoodsI/StoreI;BackpackDef |
| 英雄 / 招募 / 装备 / 宠物 / 专家 | hero, hero_train, equip, recruite, pet, expert, lordcollection |
hero_HeroC/HeroD, equip_heroequipc, recruite_RecruitC, pet_PetC/PetAdventureC, expert_ExpertC;HeroEquipDef/RecruitDef/LordEquipmentDef/LordCollectionDef/ExpertTravelDef |
| 战斗 / 军队 / 兵种 | soldier, arena, brawlarena, climbtower, worldboss, purifying |
soldier_SoldierC/SoldierD/BarrackC, arena_ArenaC, climbtower_ClimbTowerC, worldboss_WorldBossC, purifying_PurifyC;SoldierDef/BarrackDef/AttributeDef |
| 联盟 / 军团 / 军械库 | alliance, allianceinfo, alliancescience, alliancedevelopment, alliancewhitelist, arsenalleague, arsenalwar, arsenalleaguehonour |
alliance_AllianceC/AllianceBuildingC/AllianceChestC/AllianceDuelC/AllianceHelpC/AllianceMemberC/AllianceMobilizationC, arsenalleague_ArsenalLeagueTaskC;AllianceDef/AllianceBossDef/AllianceDuelDef/AllianceScienceDef/AllianceVoteDef/ArsenalWarDef/ArsenalLeagueCompetitionDef |
| 世界地图 / 行军 (March) | world, world_march, world_mapobj, capitalwar |
world_WorldMapC/WorldMapCapitalC/WorldMapFogC/WorldMapFortC/WorldPlayerC/WorldAutoAttackMonsterCtrl;world_march_WorldMarchC,WorldMarchDataFactory,WorldBaseMarchD;world_march_map_MapAttackCityMarchD,MapCollectMarchD;world_mapobj_data_MapMonsterD/MapResourceD/MapCapitalD;CapitalPresident;WorldMarchDef/WorldMarchDefine/SupremacyWarMapDef/WorldBossDef/MineWarMapDef/BrawlWarMapDef/TriangleWarMapDef/ArsenalWarMapDef |
| 活动 / 运营(最大域) | activity + 129 个 activity_data_*(activity_data_ 前缀下 129 个去重关键词) |
activity_ActivityC, activity__eventcalendar, activity_DivinationRoomCtrl, activity_PinballC, activity_MineWarC, activityaccumulative_AccumulativeActivityC;ActivityDef/CalendarDef/PolarExpeditionDef/PickDef/TrialOfSurvivalDef/DigTreasureDef |
| 社交 / 聊天 / 邮件 / 好友 | chat, mail, friend, favorites, playercard, opinion |
chat_ChatC/ChatD/ChatCtrl/ChatProxyC/ChatGroupVoteC, mail_MailC/MailD/MailShareD, friend_FriendC, favorites_FavoritesC;ChatDef/OpinionDef/PresetEmojiDef |
| 抽卡 / 转盘 / 扭蛋 | heroturntable, festivalturntable, slotmatrix, pitchpot, monopoly |
heroturntable_HeroTurntableC, slotmatrix_SlotMatrixC/SlotMatrixBroadcastD, pitchpot_PitchPotGameC, monopoly_MonopolyC;HeroTurntableDef/FestivalTurntableDef/SlotMatrixDef/MonopolyDef |
| 任务 / 引导(BTree) | guide, mission, radar, todo, development + game_module_btree_* |
guide_GuideC, mission_MissionCtrl/MissionData, radar_RadarQuestC, todo_ToDoListC, BTreeNodeCtrl;RadarDefineF/ToDoListDef;545 张 game_config_btree_guide_* 表 |
| 小游戏 / 休闲 | fishing, garden, gardenguide, knifehit, findinggame, puzzle, minigame, snowclean, duckmarket, pitchpot |
fishing_FishingCompetitionC/FishingRankC, garden_GardenC/GardenGodownBaseC/LifeTreeD/LogCampD/SawmileD, snowclean_SnowCleanC, duckmarket_DuckMarketC;FishingCompetitionDef/SnowCleanDef/SnowFundDef/FindingGameDef/KnifeHitDef |
| 科技 / 养成 / 数值 | science, refinescience, firecrystalscience, research, buff, splitasset |
science_ScienceC, purifying_PurifyC, buff_BuffC, splitasset_SplitAssetCtrl;RefineScienceDef/FireCrystalLeakDef/AttributeDef |
| 经济 / 商业化 | pay, recharge, rechargeshop, fivedaysrecharge, continuousrecharge, bank, welfare, vip, supervip, gift, customgift, dailydeal |
pay_PayC, bank_BankC, vip_VipPrivilegeC, supervip_SuperVipC;VipPrivilegeDefine/RechargeDefin/RechargeShopDef/MonthCardDefine/CustomGiftDef/OptionalGiftDef |
| 系统底座 | system, login, settings, abtest, versionup, translate, unlock, rank, overview, cheat |
system_HeartbeatC/OnlineC/SubHeartbeatC, login_LoginC/LoginUnlockC, settings_RecordHttpManager/LikeC, abtest_ABTestC, translate_TranslateCtrl, rank_RankC/RankD, overview_OverviewWorldMap…(31 个 overview 家族), cheat_CheatC/SimulationNet…;SettingsDef/LockConditionDef/ViewHelperDef |
逻辑代理层(game_module_logic_proxy_,共 7 组):activity_ArsenalLeagueGuessProxy / MagicPotionProxy / MineWarProxy、fishing_FishingProxy、minewar_MineWarMapProxy、pet_PetAdventureProxy、redpacket_RedPacketProxy、thanksgiving_ThanksgivingProxy、trianglewar_TriangleWarMapProxy —— 说明存在跨服/跨活动数据桥接的独立代理层。
2. 网络协议地图(protocol_ops.txt,2,635 条)#
消息名前缀规范:req_*(请求)/ notify_*(服务端推送)/ 少量 res_*、ack_*、rsp_*、push_*、sync_*。操作码(opcode)分布于多个区间段(如 1xxx 基础、5xxx 联盟/世界、9xxx–1xxxx 活动/王城、2xxxx–3xxxx 新版运营)。
方向统计(受定长截断影响,为可见值):req_* ≈ 1,083,notify_* ≈ 514,其余为前缀被截断条目。
2.1 域统计表(关键词匹配;跨域可能重叠)
| 域 | #op | 代表 op(操作码) |
|---|---|---|
| alliance 联盟 | 292 | notify_alliance_member_pos_update(5179)、req_alliance_whitelist_add(5147)、req_alliance_showdown_invite_apply(9810)、req_alliance_change_leader(5113) |
| arsenal 军械库/联盟战争 | 123–140 | req_alliance_arsenal_war_dispatch(11905)、req_arsenal_league_wall_outfire(11854)、req_alliance_arsenal_league_sign_up(30524)、req_arsenal_league_guess_bet_champion(30474) |
| supremacy 霸权战 | 121 | notify_supremacy_war_alliance_info_update(20651)、req_supremacy_war_task_reward(16571)、req_supremacy_war_extra_situation_info(16629) |
| world / march 世界·行军 | 157 | req_world_march(5701)、req_world_march_speedup(5703)、req_world_mass_master_detail(5708)、notify_world_player_init(5651) |
| quest 任务 | 85 | req_mobilization_get_quest(10151)、req_back_quest_get_score_reward(15152)、req_get_chapter_award(3901) |
| rank 排行 | 74 | req_most_lords_rank(10502)、req_cross_most_lords_rank(12803)、req_frostfire_war_center_rank(13316) |
| sign / 战令(BP) | 66 | req_general_bp_get_all_reward(15472)、req_back_bp_get_all_reward(15202)、req_thanksgiving_bp_get_all_reward(15682) |
| spectator 观战 | 63 | req_spectator_supremacy_war_enter(17001)、rsp_watch_capital_war_info(10914) |
| capital 王城战 | 62 | req_capital_war_add_schedule_task(10932)、req_capital_war_set_office_mode(10940)、notify_capital_war_president_data(10957) |
| chat 聊天/群 | 48 | req_chat_players_info_by_key(3418)、req_chat_group_agree_member(13565)、req_chat_group_vote_select(17575) |
| battle 战斗 | 47 | req_slg_3_league_get_battle_report(17623)、notify_arsenal_league_battlefield_result(17263) |
| fort 要塞 | 35 | req_fortwar_goal_data(10716)、req_fortwar_apply_data(10715)、notify_fortwar_activity_init(10761) |
| triangle 三角战 | 46 | req_triangle_war_enter_warzone(15981)、req_triangle_war_march_rush(15992)、req_alliance_triangle_war_match_score(15936) |
| brawl 乱斗 | 42 | req_brawl_war_player_record(17503)、req_brawl_war_move_city(17433)、req_brawl_war_wall_outfire(17442) |
| monopoly / linkage 大富翁·联动 | 42 | req_monopoly_play_gold_dice(13652)、req_linkage_monopoly_get_apply_list(30752)、notify_linkage_monopoly_play_dice_info(30786) |
| frostfire 霜火战 | 28 | notify_frostfire_war_player_num(13374)、notify_frostfire_hero_injured(13377) |
| mail 邮件 | 27 | req_mail_collect(2504)、req_mail_cover_list(2501) |
| expert 专家 | 25 | req_expert_send_gift(30432)、req_expert_skill_research(30435) |
| hero 英雄 | 24 | req_hero_promote_star(1812)、req_hero_debris_exchange(1814)、req_hero_search_times(4701) |
| monster siege 怪物攻城 | 12–24 | req_monster_siege_personal_history(10214)、notify_monster_siege_wave(10206)、notify_monster_siege_endtime(10205) |
| recharge / pay 充值 | 22–23 | req_recharge_lottery_draw(20281)、notify_recharge_lottery_info(20291)、notify_recharge_fail(6704) |
| vip 特权 | 22 | req_vip_privilege_get_red_packet_reward(16174)、req_vip_get_daily_exp(4651) |
| showdown 联盟对决 | 20 | req_alliance_showdown_reorganize(9807)、req_alliance_showdown_battle_wave_report(9841) |
| pet 宠物 | 19 | req_pet_break(15322)、req_pet_adventure_dispatch(15541) |
| truck escort 卡车护送 | 19 | req_truck_escort_plunder_fight(16426)、req_truck_escort_refresh_enemy_list(16440) |
| migrant 移民 | 19 | req_migrant_set_power_limit(15851)、req_migrant_get_act_history(15863) |
| snow 除雪/基金 | 18 | req_snow_clean_handbook_draw(30238)、notify_snow_fund_purchase(17126) |
| fishing 钓鱼 | 16 | req_fishing_gear_upgrade(13502)、req_fishing_get_free_gift(13507) |
| arena 竞技场 | 14 | req_arena_get_opponent(8460)、req_arena_rank_top(8483) |
| recruit 招募 | 12 | req_recruit_hero(1813)、notify_recruitment_loyalty(4203) |
| red packet 红包 | 6 | req_red_packet_send(17171)、req_red_packet_get(17173)、req_red_packet_refresh(17174) |
| cheat 作弊/调试 | 218 | notify_cheat_record_info(1300)、req_cheat_inject_lua_script(14094)… |
2.2 值得关注的协议
| 主题 | op(操作码) | 说明 |
|---|---|---|
| 充值抽奖 | req_recharge_lottery_draw(20281) / notify_recharge_lottery_info(20291) |
累计充值抽取型付费玩法 |
| 作弊记录上报 | notify_cheat_record_info(1300)、req_get_cheat_record_info(1261) |
服务端向客户端下发“作弊记录” |
| 作弊/调试指令 | req_cheat_inject_lua_script(14094)、req_cheat_query_database(1205)、req_cheat_query_player_data(14101)、req_cheat_add_total_recharge_diamond(14169)、req_cheat_change_sys_time(1223)、req_cheat_switch_wormhole(1284)、req_cheat_uproot_all_multiverse_city(1296)、req_cheat_hotfix_config(14163)、req_cheat_publish_kingdom_channel(14044)、req_cheat_send_mail(14027) |
名为 cheat_ 的完整 GM 指令集(218 条),覆盖改数值、跳关、发邮件、切时间、注入脚本 |
| 联盟对决 | req_alliance_showdown_invite_apply(9810)、…reorganize(9807)、…battle_wave_report(9841)、req_cheat_alliance_showdown_set_grade(14133) |
联盟 PvP 赛制 |
| 军械库联盟战 | req_alliance_arsenal_send_request_mail(11912)、req_alliance_arsenal_war_dispatch(11905)、req_arsenal_league_honor_mvp_info(30633)、req_cheat_arsenal_league_rematch(14160) |
跨服军械库联赛 |
| 王城战 | req_capital_war_add_schedule_task(10932)、req_capital_war_set_office_mode(10940)、req_capital_war_appoint(10903)、notify_capital_war_president_data(10957) |
总统/官职体系 |
| 怪物攻城 | req_monster_siege_personal_history(10214)、req_monster_siege_get_score_reward(10213)、notify_monster_siege_wave(10206)、req_cheat_monster_siege_set_diff(14048) |
波次攻城 |
| 挖宝 | notify_dig_treasure_info(20660)、req_dig_treasure_start_level(20652)、req_cheat_dig_treasure_jump_level(14083) |
挖宝关卡 |
| 红包 | req_red_packet_send/get/refresh/get_detail(17171/17173/17174/17175) |
联盟/世界红包 |
| 老虎机广播 | notify_slot_matrix_broadcast_records(30658)、req_cheat_slot_matrix_add_broadcast(14207)、rsp_slot_matrix_watch_records(30657) |
抽奖广播 + 观战 |
| 霸权战 | notify_supremacy_war_alliance_info_update(20651)、req_spectator_supremacy_war_enter(17001)、req_cheat_supremacy_war_gov_global_test(14075) |
全服争霸 + 观战 |
| 找货/场景寻宝 | req_find_goods_scene_mark(20933)、notify_find_goods_room_member_join(20972) |
房间制寻宝玩法 |
| 魔法药水 | req_magic_potion_submit(21032)、notify_magic_potion_init(21031) |
拼图/合成类活动 |
3. UI / 视图分类(ui_views.txt,6,977 个)#
3.1 类型后缀分布
| 后缀 | 数量 | 后缀 | 数量 |
|---|---|---|---|
View |
3,244 | SubPanel |
710 |
Panel |
1,976 | Ctrl |
618 |
SubView |
386 | 其它(PopPanel/PopUp…) |
40 |
| 合计 | 6,977 |
3.2 功能分类(关键词归类,存在跨类重叠)
| 类别 | 约数 | 代表视图名 |
|---|---|---|
| Battle 战斗 | ~1,436 | AllianceDuelBattlePanel、AllianceDuelBattleSubPanel、BattleStartCountdownPanel、ArsenalWarMapCtrl |
| Activity 活动 | ~1,312 | AccumulativeActivityCtrl、AccumulativeBigPanel、ActivityDropHalloweenSubPanel、FestivalTurntable… |
| World / March 世界行军 | ~902 | AllianceMassMarchInviteView、AllianceSituationMarchCtrl、WorldMapStaticExpressionShowView、WorldPersonalSituationMarchListBaseView |
| Alliance 联盟 | ~592 | AllianceAnnouncementOverviewView、AllianceMemberBatchOperateView、AllianceListAddRelationshipView |
| Hero / 英雄装备宠物 | ~482 | ArenaDefenseHeroDetailPopUpView、AllianceHonorLeagueStarDetailTipView |
| Treasure / 休闲玩法 | ~417 | Anniversary2025FrozenTreasureTaskPanel、BalloonDetailPopupView、DigTreasure… |
| Shop / 商业化 | ~400 | AccRechargeLotteryGetMorePopupView、AccumulativeRechargePanel、RechargeShop… |
| City / 城市建造 | ~322 | AllianceBuildingCtrl、AllianceCityMovePromptView、ArsenalWarFreeCityMoveView |
| Settings / 账号系统 | ~198 | AccountBindSubPanel、AccountCommunityPanel、ABTestCtrl、AllianceHelpView |
| Chat / 社交消息 | ~160 | ChatAllianceContentPanel、ChatAllianceSetPopupView、AllianceWarMailView |
| Login / 选择服务器 | ~148 | AllianceLanguageSelectView、CapitalWarFlagSelectView、BoxSelectBoxView |
架构观察:以 Panel / View / SubPanel / SubView 的四级组合为主,配合 Ctrl 控制器(618 个)——典型 View(表现)+ Ctrl(逻辑)+ Data/Model(数据) 分层;SubPanel/SubView 说明大量复合视图采用父子嵌套。
4. 客户端架构说明#
4.1 分层模型(Lua MVVM/ Ctrl)
从命名可确认的分层:
- Controller / Ctrl:如
WorldMapCapitalCtrl、ChatProxyCtrl、GardenCtrl、BTreeNodeCtrl、AllianceBriefInfoCtrl;tokens.txt中Ctrl型类共 21+ 处,且存在DataMgr(58)、Proxy(39)、Mgr等管理类。 - Data / Model(
*D/*Data):如world_march_WorldMarchDataFactory、hero_HeroD、soldier_SoldierD、garden_GardenD/GardenSceneryD/LifeTreeD、rank_RankD、chat_ChatD。数据与表现分离,*D为纯数据,*C/Ctrl为逻辑。 - Interface(
*I):queue_QueueI、store_StoreI/StoreGoodsI、alliance_AlliancePlayerI、AllianceMobilizationI、world_march_IWorldMarchD——接口/契约定义。 - Proxy(代理):
chat_ChatProxyC/ChatProxyCtrl、game_module_logic_proxy_*——跨模块通信与跨服桥接。 - View 层:
game_module_ui_view_*(3,222) +game_module_ui_item_*(4,892) +game_module_ui_component_*(12),由ViewHelperDef统一。 - 事件/消息:常量中出现
SendMsg/SendMessage/OnRes…/Request…(如WorldMapCapitalCtrl:ReqWatch、WorldMapExpressionCtrl:RequestSendMarchExpression、AllianceMemberBatchOperateView:_RequestPlayerPointAsyncByPlayerIds),即 Ctrl 内以Req*/OnRes*方法收发协议。
4.2 模块定义(game_define_moduleDefine_*)
game_define_moduleDefine_* 共 128 条(如 ActivityDef、AllianceDef、AllianceDuelDef、ArsenalWarDef、WorldMarchDef、ConstructDef、HeroEquipDef、RecruitDef、SoldierDef、ChatDef、VipPrivilegeDefine、SettingsDef)。这与 game_module_logic_module_* 家族一一映射,构成模块注册/解耦表——游戏按模块装配,模块即玩法单元。
4.3 配置表(config_tables.txt,1,600 张)
| 前缀 | 数量 | 用途 |
|---|---|---|
game_config_default_* |
999 | 数值/静态配置:construct_*、building_*、alliance_duel_*、alliance_building_attr/cost、active_recharge_* 等 |
game_config_btree_guide_* |
545 | 行为树引导脚本(Guide_Animal_Attack、Guide_Construct_Furnace_Accelerate、Guide_DigTreasure_Plan…);分 Guide_* / weakguide / weakguide_jump 三系 |
game_config_custom_* |
47 | 关卡/场景自定义数据(custom_findinggame_find_level_scene_08、custom_garden_layout、custom_snowclean_*) |
配置与逻辑分离,game_config_btree_* 表明新手引导 = 数据驱动行为树(对应 game_module_btree_action/precondition/ctrl_* 的 Guide/AI 节点)。
4.4 内嵌协议 DSL(文本)
lua_strings_32.txt 中直接包含可读的协议字段定义 DSL(request { … } / response { … }),形如:
notify_player_city_update 5656 { #
request { # …
def 1 : owner #
owner 0 : integer #
owner_kid 30 : integer #
owner_uid 5 : integer #
字段语法为 <字段名> <编号> : <类型>,支持 *类型()(嵌套/数组)、integer(N)(定长)、boolean、string,并可带 # 注释;request / response 分块对应请求与应答。该 DSL 是运行期按文本解析生成的协议编解码表(与 protocol_ops.txt 的操作码表互补),说明网络层为自研文本化 protocol schema(非 protobuf .proto,但语义等价)。
4.5 AI / 表现层
- 分层状态机 HFSM:
game_module_hfsm_state_*(monster_attack/pursue/flee/guard、garden_sawwood/choptree/goSaw、fishing_cast/hole、towerdefence_attack/face/gotarget、pet_action)+hfsm_condition_*——世界地图单位与场景小游戏的行为由 HFSM 驱动。 - 行为树 BTree:
game_module_btree_ctrl_*(BTreeNodeSequence/Selector/PrioritySelect/Parallel)、btree_action_guide_*(FrameClick、ScenePlay、Bubble、JumpToConstruction…)、btree_precondition_guide_*/ai_*。引导与 AI 共用同一行为树框架。 - 场景模块:
game_module_scene_world(413)、snowclean(389)、battle(195)、arsenalwar(101)、towerdefence(70)、minewar(70) 等,负责地图/单位/摄像机表现。
5. 风险与攻击面#
| 风险项 | 证据 | 评估 |
|---|---|---|
| 客户端权威逻辑(Client-Authoritative) | 存在 game_module_ui_view 3k+ 表现类与大量 *C/Ctrl 逻辑类同处 Lua 层;WorldMarchDataFactory、WorldMapObjDataCls 等在地图/行军中承担推演 |
关键战斗/行军结算若在客户端预演,前线进程易被篡改;需服务端二次校验(存在 battle_report/record 协议,倾向服务端裁决,但客户端仍持有完整规则与配置) |
内置 GM/作弊指令集(218 条 cheat_ 协议) |
req_cheat_inject_lua_script(14094)、req_cheat_query_database(1205)、req_cheat_query_player_data(14101)、req_cheat_add_total_recharge_diamond(14169)、req_cheat_change_sys_time(1223)、req_cheat_send_mail(14027)、req_cheat_publish_kingdom_channel(14044)、req_cheat_hotfix_config(14163) |
最高危面:完整改数值/发资源/按 UID 查询/注入 Lua/热更配置入口全在客户端可达(是否服务端二次鉴权未知)。攻击者可尝试伪造这些 req_cheat_* 包越权 |
| 作弊记录通道 | notify_cheat_record_info(1300)、req_get_cheat_record_info(1261)、req_cheat_* 中大量“端到端记录”(…op_privacy_policy、…record) |
服务端有反作弊检测与下发机制,但检测规则与 cheat_ 指令同客户端,攻击者可解析上报逻辑进行规避 |
| 回放 / 观战 / Botting | req_spectator_supremacy_war_enter(17001)、rsp_slot_matrix_watch_records(30657)、notify_slot_matrix_broadcast_records(30658)、req_brawl_war_player_record(17503)、req_alliance_showdown_battle_wave_report(9841)、req_slg_3_league_get_battle_report(17623) |
完整的 replay/观战/战斗报告 数据链路 → 可录制→重放/脚本化,或用于自动挂机(botting) |
| 概率玩法(抽奖类) | req_recharge_lottery_draw(20281)、req_hero_turntable_*(10352/10353/10354)、req_duck_market_gacha(21001)、req_sign_draw_lottery(16132)、req_festival_turntable_shop_buy(20645)、slotmatrix 系列 |
转盘/扭蛋/老虎机集中在客户端触发,需确认掉率与结果由服务端决定;客户端可见 game_config_* 概率表(若配置在客户端则可被读取) |
| 经济与付费完整性 | req_cheat_add_total_recharge_diamond(14169)、req_beta_test_recharge(6791)、notify_recharge_fail(6704)、game_config_active_recharge_* |
付费/赠送路径存在可测协议;cheat_add_total_recharge_diamond 若鉴权不严可刷充值额度 |
| 定时/时钟依赖 | req_cheat_change_sys_time(1223)、notify_activity_calendar_data(9925)、notify_monster_siege_endtime(10205)、大量 *_cd_endtime / *end_time 字段 |
客户端存在基于本地时间的 CD/活动判定(有 change_sys_time 调试口),需确认关键时间由服务端时间戳约束 |
| 跨服/赛季状态 | req_cheat_merge_kingdom(14061)、req_cheat_switch_wormhole(1284)、req_cross_capital_*、req_mergekingdom_*、notify_merge_milestone_preview_info(20233) |
合服/跨服/赛季迁移逻辑复杂,是状态一致性攻击面 |
| 协议 schema 客户端可见 | lua_strings_32.txt 中明文 request/response DSL + protocol_ops.txt 操作码表 |
攻击者可完整重建协议编解码,构造任意报文(含 cheat_ 报文),降低协议逆向门槛 |
结论:游戏采用 「Lua MVVM/Ctrl + 自研文本协议 DSL + 数据驱动配置(含 BTree 引导/HFSM AI)」 的清晰分层架构,玩法模块化程度高(196 个逻辑家族 / 128 个模块定义 / 1600 张配置)。安全上最突出的特征是:完整的 218 条 cheat_ GM 指令集直接存在于客户端可达的协议表中,连同 notify_cheat_record_info 反作弊通道与完整的观战/回放链路,构成主要的越权与自动化(botting)风险面。玩家侧权威边界需依赖服务端对 req_*(尤其非 req_cheat_* 的正常请求与 req_cheat_*)做严格二次校验,否则客户端可凭借可见的协议 schema 与配置表大幅降低攻击成本。
本分析仅基于脱壳样本内的只读符号/字符串证据,未对服务器发起任何真实请求;所有 opcode 与类名均为证据原文,未做推测性命名。
10 · 数值 / 配表(CONFIG / NUMERIC DATA)分析#
目标:com.gof.china 1.15.6(Whiteout Survival CN /《无尽冬日》,Unity 2022.3.52f1 + IL2CPP + ToLua)。
证据(全部只读):
- artifacts/lua/config_tables.txt — 1,600 个 game_config_* 表名
- artifacts/lua/tokens.txt — 252,026 条 Lua 标识符(前缀树展开)
- artifacts/lua/lua_strings_32.txt — 386,532 行原始常量(含大量 Lua 源码片段)
- artifacts/out_types.txt / out_identifiers.txt / out_literals.txt — IL2CPP 元数据
- extracted/Payload/whiteoutsurvivalcn.app/Data/Raw/ — 随包资源
本文所有数字 / ID 均直接来自上述文件;无法确证的推断已明确标注「推断」。
0. 结论速览#
| 问题 | 结论 |
|---|---|
| 配表如何组织 | 以 Lua 表名 game_config_<命名空间>_<表> 引用;三大命名空间 default / btree_guide / custom |
| 表名是否随包 | 是。1,658 处 game_config_* 作为 Lua 字符串常量出现在本地加密的 luapackage_32/64 内 |
| 真实数值是否随包 | 没有以明文形式随包。本地 5,316 个 .unity3d 中仅 14 个 config_*,且全是「映射类」配置(图集/音效/语言/登录),不含英雄/建筑/士兵数值表;Data/Raw/world/*.bytes 只有大地图配置 |
| 数值去向 | 远程版本化资源管线(CDN https://gofcn-prod-cdn.campfiregames.cn/RELEASE/{0}/{1}/)+ 二进制常量(嵌在加密 Lua 字节码/远程资源中);见 §4、§5 |
| 属性系统 | data_map[id] 聚合公式(id∈40001..40028)+ attr_type 描述键(attr_type_desc_10101..10616、40022..40026、60001/60002) |
| 可否被攻击者篡改 | 可以:表名与公式在本地且容器明文,仅首块 XOR + EncryptLuaBytecode;但服务端数值不在本地,纯本地改表只会与服务端不一致(见 §5.3) |
1. 配置表分类(Config Taxonomy)#
1.1 命名方案
表名统一格式:game_config_<Namespace>_<Table>,其中 <Namespace> 只有三类:
| 命名空间 | 语义 | 数量(含前缀重复) |
|---|---|---|
game_config_default_* |
主数据配表(玩法/经济/养成/活动) | 1,000 |
game_config_btree_guide_* |
行为树(BTree)新手引导配表 | 547 |
game_config_custom_* |
场景/关卡自定义地图配表 | 47 |
game_config_(裸前缀)+ 少量截断 |
提取噪声 | 6 |
game_config_btree_guide_* 的证据:在 lua_strings_32.txt 中可读到编辑器源路径
Assets/Scripts/editor/modules/ai/BTreeEditor/Config/guide/Guide_Test_1.json
Assets/Scripts/editor/modules/ai/BTreeEditor/Config/guide/Guide_Monopoly*.json
Assets/Scripts/editor/modules/ai/BTreeEditor/Config/ai/Hunter.json
Assets/Scripts/editor/modules/ai/BTreeEditor/Config/ai/Cooker_Idle.json
Assets/Scripts/editor/modules/ai/BTreeEditor/Config/ai/Hero_Patrol.json
Assets/Scripts/editor/modules/ai/BTreeEditor/Config/gui/*.json
即这些是 AI 行为树编辑器导出的 JSON,表名即 BT 入口节点名,前缀 Guide_* / WeakGuide_*(弱引导)。目录 ai/ 放 NPC AI(Hunter/Cooker/Hero_Patrol),guide/ 放引导,gui/ 放 UI 流程。bundle 内可见前缀树节点如 btree_precondition_guide_guide_istrigger_condition、btree_preconditionlogicopt_btreenodepreconditionand。
game_config_custom_* 的 19 个叶子名(场景级数据):
game_config_custom_findinggame_find_level_scene_{9,19,08,105,110}
game_config_custom_garden_lay…
game_config_custom_snowclean_snowclean_{cu…,entity_con…,map_20,map_10021,map_10032,map_10043,map_10111,map_10115,map_10122,map_101148,map_10125n}
game_config_custom_trianglewar_map_build…
1.2 前缀树 / 截断说明(重要)
config_tables.txt 的 1,600 个名字中,440 个是另一个名字的严格前缀,例如 game_config_default_al → game_config_default_alliance。这是 tokens.txt 采用前缀树展开(每个标识符的每一段前缀都作为独立 token 输出)造成的提取产物,不是游戏里真有多张短名表。
因此:
- 名义表名 1,600;
- 去重后的「叶子」完整表名 1,160(其中
default708、btree434、custom18)。
下文计数同时给出 总数 / 叶子数。
1.3 家族分类计数
按表名关键字归族(一条表名归入首个命中的家族,故各家族互不重叠,合计 = 1,600 / 1,160):
| 家族 | 总/叶子 | 可能治理的内容 |
|---|---|---|
btree_guide 新手引导行为树 |
547 / 434 | 引导触发条件、章节解锁、引导跳转、AI 行为树 |
gift/recharge/pack 充值礼包 |
56 / 42 | 充值档位、礼包、通行证(BP)、限时特惠 |
snow 冰雪/铲雪 |
50 / 28 | 暴风雪、铲雪小游戏、雪灾参数 |
arsenal 军备/兵工厂 |
43 / 25 | 军备联赛(3v3 车战)、兵工厂战场 |
sdk/network/系统 |
41 / 26 | SDK 错误、推送、屏蔽词、语言翻译、账号删除、服务器流程 |
supremacy 霸权战 |
39 / 28 | 霸权战阶段/奖励/官职 |
frozen/icefield/冰原 |
39 / 26 | 冰原狩猎、极地远征、寻宝 |
arena/pvp 竞技 |
36 / 28 | 竞技场、爬塔、矿战、决斗、跨服联赛 |
task/activity 任务活动 |
35 / 30 | 任务、章节、活动 |
fishing 钓鱼 |
29 / 18 | 钓鱼小游戏、鱼种、积分 |
expert/travel 专家旅行 |
29 / 21 | 专家系统、旅行玩法、专家技能 |
festival 节日/通行证 |
28 / 22 | 万圣节/情人节/春节/圣诞/龙舟/中秋等节日 |
item/equipment 物品装备 |
25 / 16 | 物品、领主装备、圣物、材料 |
capital/fort 首都/要塞 |
24 / 17 | 首都战、要塞战、赛季目标 |
monopoly 大富翁联动 |
23 / 17 | 联动大富翁关卡 |
cross 跨服 |
23 / 16 | 跨服战、跨服王者 |
world 大世界 |
23 / 18 | SLG 大地图、城市交互、宠物捕捉 |
thanksgiving 感恩节 |
23 / 15 | 火鸡大餐、烹饪 |
hero 英雄 |
22 / 18 | 英雄品质/技能/专武/招募转盘 |
garden 花园 |
21 / 10 | 花园建造、繁荣度、建筑属性 |
gacha/lottery 抽奖 |
28 / 19 | 老虎机、弹珠、转盘、福卡、魔法药水、签到抽奖 |
anniversary 周年庆 |
18 / 13 | 周年回顾、周年展示 |
daily 每日 |
18 / 12 | 每日任务/礼包/积分 |
frostfire 霜火 |
17 / 12 | 霜火之战 |
monster/pve 怪物 |
16 / 13 | 怪物攻城、PvE Boss、赏金 |
brawl 混战 |
14 / 11 | 混战战场、空投、地形 |
strongest 最强 |
14 / 9 | 最强领主 |
tech/research 科技 |
13 / 8 | 科技树、精炼科学、火晶科学 |
intel 情报 |
11 / 9 | 情报任务、雷达、赏金 |
building/city 建筑城市 |
10 / 8 | 建筑、炉子、使馆、城内交互 |
chat/social 社交 |
9 / 8 | 聊天、表情、社交 |
map/terrain 地图 |
10 / 8 | 地图、地形、场景 |
pet 宠物 |
7 / 6 | 宠物捕获/喂养/洗炼 |
survivor/status 幸存者状态 |
6 / 6 | 幸存者状态、忠诚度 |
mercenary 佣兵 |
3 / 3 | 佣兵副本 |
soldier/army 士兵 |
2 / 2 | 兵营训练 |
misc/其他 |
181 / 107 | 其余小系统(见下) |
misc/其他 内含若干独立小玩法与系统,例如:duckmarket_*(鸭子市场)、fortune_card_*(福卡)、pitchpot_*(投壶)、puzzle_*(拼图)、treasure_hunt_*(寻宝)、truck_escort_*(卡车护送)、slot_matrix_*、pachinko_*、three_tiles_*、magic_potion_*、monster_siege_*、most_lords_*、mobilization_*、popular_star_*、showdown_*、slg_3_league_*、spar_laboratory_*、fire_crystal_*、mercenary_*、spring_couplets_*、shengdan_*、knockdoors_*、wheel_fortune_*。
代表性 default 表名举例:game_config_default_hero_skill_upgr…、game_config_default_hero_equipment_stren…、game_config_default_lords_equipment_config_、game_config_default_lords_gem_config、game_config_default_technology_tree_con…、game_config_default_skill_config_mapp…、game_config_default_alliance_building_cost、game_config_default_fishing_level_con…。
说明:表名在 config_tables.txt 中被截断(如 _con… / _gr… / _rew…),这是提取层的前缀/截断产物,不是游戏真实命名。
2. 数值结构:内嵌的 Lua 属性聚合公式#
lua_strings_32.txt 中直接包含多段属性取值函数源码。经原始字节解析(键后紧跟一个长度字节 + UTF‑8/ASCII 值),可还原为:
local s = function(data_map) return (data_map[40002] or 0) + (data_map[40014] or 0) end return s -- → EventDuring
local s = function(data_map) return (data_map[40003] or 0) + (data_map[40015] or 0) end return s -- → Sleep
local s = function(data_map) return (data_map[40004] or 0) + (data_map[40016] or 0) end return s -- → Comfort
local s = function(data_map) return (data_map[40005] or 0) + (data_map[40017] or 0) end return s -- → Mood
local s = function(data_map) return (data_map[40006] or 0) + (data_map[40018] or 0) end return s -- → Satiety
local s = function(data_map) return (data_map[40007] or 0) + (data_map[40019] or 0) end return s -- → Health
local s = function(data_map) return (data_map[40008] or 0) + (data_map[40020] or 0) end return s -- → CureRate
local s = function(data_map) return data_map[40010] or 0 end return s -- → MEAT_SAFE
local s = function(data_map) return data_map[40028] or 0 end return s -- → LawToken
2.1 幸存者/城镇属性公式表(14 个具体 ID)
| # | 属性名 | data_map key 组合 | 公式(原文) |
|---|---|---|---|
| 1 | EventDuring |
40002 + 40014 | (data_map[40002] or 0)+(data_map[40014] or 0) |
| 2 | Sleep |
40003 + 40015 | (data_map[40003] or 0)+(data_map[40015] or 0) |
| 3 | Comfort |
40004 + 40016 | (data_map[40004] or 0)+(data_map[40016] or 0) |
| 4 | Mood |
40005 + 40017 | (data_map[40005] or 0)+(data_map[40017] or 0) |
| 5 | Satiety |
40006 + 40018 | (data_map[40006] or 0)+(data_map[40018] or 0) |
| 6 | Health |
40007 + 40019 | (data_map[40007] or 0)+(data_map[40019] or 0) |
| 7 | CureRate |
40008 + 40020 | (data_map[40008] or 0)+(data_map[40020] or 0) |
| 8 | MEAT_SAFE |
40010 | data_map[40010] or 0 |
| 9 | (未命名,dump 丢失) | 40011 | data_map[40011] or 0 |
| 10 | (未命名,dump 丢失) | 40012 | data_map[40012] or 0 |
| 11 | (未命名,dump 丢失) | 40013 | data_map[40013] or 0 |
| 12 | (未命名,dump 丢失) | 40027 | data_map[40027] or 0 |
| 13 | LawToken |
40028 | data_map[40028] or 0 |
| 14 | IRON_SAFE / COAL_SAFE / WOOD_SAFE |
(与 MEAT_SAFE 同区常量) |
仅见常量名,未见函数体(推断为 40011–40013 一类) |
关键结构:第 1–7 项构成 7 组「基础项 + 加成项」求和,偏移恒为 +12:40002↔40014 … 40008↔40020。这对应「基础属性 + 额外/加成属性」两套数值;40001(对应 40013)在公式中未成对出现,40009(对应 40021)亦未见。MEAT_SAFE 等属「被保护资源量」类(肉/铁/煤/木的保鲜/保护上限)。
名称 EventDuring 在 tokens.txt 中亦独立存在(另有 SetEventDuringAndRemainingMti、GetEventDuring 等),确认为幸存者「事件持续」类属性。
2.2 部队/士兵战斗属性(*_increase / *_reduce)
tokens.txt 中存在一套战斗属性键(作为表列名/加成类型名),同一命名模式:
soldier_atk_increase / soldier_def_increase / soldier_hp_increase / soldier_dmg_increase
infantry_atk_increase / archer_hp_increase / garrison_def_increase
enemy_soldier_atk_reduce / enemy_infantry_hp_reduce / enemy_cavalry_dmg_reduce
soldier_atk_increase_by_duty / soldier_atk_increase_by_pet / soldier_atk_increase_by_supremacy_title
soldier_atk_increase_by_supremacy_skill / soldier_hp_increase_by_supremacy_tower
garrison_atk_increase_in_territory / soldier_monster_atk_increase
主力计算函数:GetSoldiersPower、GetFormationSoldierFightingCapacity、GetFormationHeroFight…、PowerBuffStatistics、PowerValue、CalculationFormulaMMMD、CalculationFormula、CalculationValue、StatisticalMagnitudeSold…、StatisticalMagnitudePowerValue。即战力(Power)由公式类动态计算,而非纯静态值。
3. 属性与 ID 命名空间#
3.1 attr_type 描述键(属性类型枚举)
tokens.txt 中 attr_type_desc_* 共 155 个不同 ID(154 个前缀 token),分块如下:
| 段 | 范围 | 用途(由同现中文/同现键推断) |
|---|---|---|
| 10xxx 主段 | 10101–10616(多段连续) |
战斗/养成/联盟/采集等属性类型 |
| 40xxx | 40022–40026 |
城镇/幸存者属性 |
| 60xxx | 60001–60002 |
城镇(含跨服/二次城)相关 |
| 特殊 | 103462 |
单例(水晶相关) |
实际连续段(用于判断子系统划分):10101–10108、10110–10116、10121–10125、10201–10216、10221–10229、10301–10348、10403–10448、10501–10531、10600–10616、40022–40026、60001–60002。可见 10200 与 10300 段明显是 10100 段的「敌方 / 守城」镜像。
可可靠还原中文描述的属性类型(示例):
| attr_type ID | 描述(原文,来自 lua_strings_32) |
|---|---|
| 10105 | 矛兵攻击力 |
| 10110 | 射手防御力 |
| 10203 | 敌方盾兵穿透力 |
| 10207 | 敌方矛兵穿透力 |
| 10213 | 敌方所有部队攻击力 |
| 10221 | 敌方攻击力减益(宠物技能) |
| 10228 | 生命值增益(宠物技能) |
| 10304 | 守城部队生命值 |
| 10310 | 敌方守城部队防御力 |
| 10311 | 敌方守城部队穿透力 |
| 10341 | 群兽洞窟专属全属性提升 |
| 10344 | 水晶技能 |
| 10404 | 研究速度 |
| 10417 | 铁矿产量 |
| 10420 | 木材采集速度 |
| 10422 | 铁矿采集速度 |
| 10436 | 常规情报任务奖励增加 |
| 10521 | 部队编组 |
| 10525 | 集结栏位 |
| 10615 | 挑战城镇:士兵损失比例上升 15% |
| 40022 | 幸存者持续工作 |
(其余大量 attr_type_desc_* 在字符串表中被截断为 1–2 字,未纳入本表,避免臆测。)
3.2 ID 命名空间总览(推断的编号体系)
| 命名空间 | 形式 | 证据 |
|---|---|---|
| 配置表 | game_config_{default,btree_guide,custom}_* |
1,600 名字,§1 |
| 城镇/幸存者属性 | data_map[40001..40028] |
§2.1 公式 |
| 属性类型 | attr_type_desc_10101..10616 / 40022..40026 / 60001..60002 / 103462 |
§3.1 |
| 物品 | item_name_<id>(如 610101、502105、100025),item_icon_<id>、item_desc_<id> |
前缀分布:30xxxx(608)、50xxxx(219)、62xxxx(197)、23xxxx、60xxxx、10xxxx、20xxxx 等 |
| 类型枚举 | FurnitureType、InteractionType、GItemShowHideType、FurnitureDefine、CityDefine、ActivityDefine、EntityWorkStatusServer |
tokens.txt 精确匹配 |
| 协议号 | 请求/通知号(如 req_survivor_sick、notify_dig_treasure_info) |
protocol_ops.txt |
3.3 数据驱动设计
- 表 → 表现的解耦:业务逻辑(Lua)只引用表名
game_config_default_*,行数据按ConfigReadHelper/ConfigMeta/ConfigName/ConfigPath读取;ConfigReadHelper提供ConvertConfigToMapWithKey、GetConfigById、GetConfigByType、GetConfigByItemId、GetConfigByLinkStr等访问器。 - 属性聚合数据驱动:属性不写死在代码,而是以「函数表」形式按
data_map[id]组合(§2.1),新增属性只改表/公式。 - 枚举驱动表现:
FurnitureType、InteractionType、CityDefine、ActivityDefine等 C# 枚举由 Lua 侧同名*Define镜像。
4. 配置加载路径#
4.1 Lua 侧加载链
lua_strings_32.txt 中出现加载链字符串:
IndexCacheCenter . GameConfig . config.ConfigMeta 需要 require
OnConfigLoadCompleted GetConfigVersion CheckConfigVersion
ConfigReadHelper ConfigMeta ConfigName ConfigPath ConfigData
含义:ConfigMeta(配置元数据)→ ConfigReadHelper(读取/转换)→ IndexCacheCenter(索引缓存)→ GameConfig(全局配置门面);并有版本校验 CheckConfigVersion / GetConfigVersion。配置值由表名索引,运行时从资源层读取。
4.2 资源 / 下载管线(C# 侧)
IL2CPP 元数据中的 CenturyGame.GameFramework.ConfigData 暴露完整远程资源参数:
Version, VersionCode, Release, UpdatePlatform, PackageCode, PackageType,
BundleId, Language, LuaArch, UseLuaJit, LuaAssetBundleMode,
DownloadThread, LoadThread, EnableAssetSplit, ResourceSplit, AssetBundleMode,
AssetCacheTime, AssetClearTime, AssetSkipPattern, TestServerList,
EnableHotFix, IsInnerTest, IsDeveloper
即客户端按 Version/VersionCode 从服务器拉取差异化资源(分片/多线程下载、热更)。
4.3 CDN 根(来自 out_literals.txt)
https://gofcn-prod-cdn.campfiregames.cn/RELEASE/{0}/{1}/
https://gofcn-prod-cdn.centurygames.cn/RELEASE/{0}/{1}/
https://gofcn-prod-cdn.campfiregames.cn/RELEASE/WebUrl_2/Web{0}.txt
https://gofcn-qa-cdn-1322302784.cos.ap-shanghai.myqcloud.com/INNER/WebUrl/Web{0}.txt
lua.unity3d
{0}{1} 为版本/分包占位符;另有平台配置下发 https://platform-config-prod.centurygame.com/config.json 与 https://clientversion.diandian.info/snapshot/。推断:数值配表随 RELEASE/{版本}/{分包}/ 的资源包下发。
4.4 本地 config_* bundle 清单(仅 14 个,全部是映射类)
Data/Raw/data/ 共 5,316 个 .unity3d(715 MB),其中以 config_ 开头的 恰好 14 个:
config_ab_mapping_6f3a862d…unity3d 配置映射(AB 分组)
config_animation2d_aee394a6…unity3d 2D 动画映射
config_atlas_mapping_b583fdba…unity3d 图集映射
config_cache_config_b0002f28…unity3d 缓存策略
config_emojiconfig_2dcd1406…unity3d 表情配置
config_game_setting_06cea4a9…unity3d 全局设置
config_language_zh_cn_7bcb37c4…unity3d 中文语言表
config_launch_prefab_809223ac…unity3d 启动 Prefab
config_login_login_logo_default_9b3a7b34… 登录 Logo
config_login_logo_01381e84…unity3d 登录 Logo
config_shadowconfig_833dbb47…unity3d 阴影配置
config_sound_mapping_5952320f…unity3d 音效映射
config_substitute_mapping_825374bd…unity3d 替代映射
config_vfxtexturelibrary_0d9e5465…unity3d 特效贴图库
→ 这些是资源/表现映射,不是数值配表。
Data/Raw/ 下唯一的非 unity3d 二进制配置是大地图 4 件套:
map_world_config_item_72ad90ca…bytes 32,942 B
map_world_config_mask_6d8d1979…bytes 720,004 B
map_world_config_setting_29d541c1…bytes 248 B
map_world_config_terrain_5199904f…bytes 526,137 B
对应 C# MapConfigHelper.LoadTerrainConfig / ReadItemData / ReadMaskData / ReadSetting / ReadTerrainChunk 与 MapItemConfig.Load、GardenConfig.Load(自研紧凑二进制 + IEEE‑754 小端浮点,非 UnityFS)。
5. 真实数值到底在哪里#
5.1 本地随包内容(确证)
| 项 | 事实 |
|---|---|
| 表名/公式 | 在本地加密 luapackage_32/64_*.unity3d(20 MB,406 块,仅首块 XOR 加密)内 |
14 个本地 config_* |
全部为映射类,无英雄/建筑/士兵数值 |
Data/Raw/world/*.bytes |
仅大地图(SLG)配置 |
5,316 个 .unity3d |
名称首段统计:ui_panel 2,403、scene_battle 687、scene_city 559、art_*、atlas_*、actor_battle 133……无任何 hero_/soldier_/building_ 数值表 bundle |
5.2 明文数值扫描结果(确证)
lua_strings_32.txt中找不到任何长数字表块(正则[0-9]+(,[0-9]+){15,}无命中);- 战斗/建筑数值未以文本形式出现。
结论(明确表述):本版本(1.15.6)没有把英雄/建筑/士兵的成表数值以明文随 IPA 分发。数值只有两条可能来源:
- 远程下发:随
https://gofcn-prod-cdn.campfiregames.cn/RELEASE/{0}/{1}/+ConfigData.Version/VersionCode拉取(强证据:CDN 根、ConfigData下载参数、CheckConfigVersion); - 二进制常量:若部分表在 Lua 侧,则数值以 LuaJIT 的双精度常量表形式存在于加密字节码中(字符串 dump 不可见,符合「无文本数值」的观察)。
两种来源可以并存;本文基于现有证据无法进一步区分某一具体数值表走哪条路径。
5.3 对攻击者的含义(可篡改性)
| 层面 | 现状 | 影响 |
|---|---|---|
| 表名 / 公式 | 本地 Lua 包,仅首块 XOR + EncryptLuaBytecode / CheckLuaBytecodeHeader |
逆向可读,可本地改公式 |
| 容器 | 明文 UnityFS,仅 block[0] 加密 | 解出首块密钥即 100% 还原资源 |
| 数值真值 | 服务端/CDN 版本化,且战力由服务端参与的公式计算 | 纯本地改表效果有限:服务端结算不一致会被检出/回滚 |
| 校验 | CheckConfigVersion、Cmd_SetConfigData、网易易盾 Enable_Encrypt_NHP 网络加密 |
改包后 ConfigVersion/校验失败风险 |
攻击面小结:客户端能改的是「表名映射、聚合公式、UI 呈现、单机小游戏参数」;核心养成/战斗数值的权威来源在服务端,静态改包无法直接刷战力,但可用于信息收集(枚举全部表名/属性 ID/公式)与本地作弊面定位。
6. 局限与后续#
- 表名被前缀树展开,1,600 名中 440 个为前缀噪声;已按 1,160 叶子统计,但个别真实长名仍可能被截断(§1.2)。
game_config_btree_guide_*为编辑器 JSON 导出,本文据源路径确认语义,未逐表解析节点。data_map[40011/40012/40013/40027]的属性名在本 dump 中丢失,仅确认 ID 存在,未臆测命名。- 属性中文描述在字符串表中部分截断,仅列出可完整还原者;完整列表需完整的 LuaJIT 常量表 dump 或台中字符串资源。
- 区分「远程下发」与「加密字节码内常量」需进一步解密并解析
luapackage的 LuaJIT 常量表(artifacts/decrypt/parse2.py方向)。
11 · 活动 / EVENTS(活动系统)分析#
目标:com.gof.china 无尽冬日(Whiteout Survival 国服)1.15.6
引擎:Unity 2022.3.52f1 + IL2CPP;业务逻辑位于 LuaJIT 字节码(luapackage_32/64),还原为 Lua 文本后抽取符号
数据来源(只读证据):
| 证据 | 规模 | 说明 |
|---|---|---|
artifacts/lua/activity_tokens.txt |
128 行 | 活动/节日标识符速览表 |
artifacts/lua/tokens.txt |
252,026 行 | Lua 标识符(含 game_module_logic_module_activity_*) |
artifacts/lua/protocol_ops.txt |
2,635 条 | 操作码<TAB>协议名(网络消息路由表) |
artifacts/lua/config_tables.txt |
1,600 张 | 配置表名(game_config_*) |
artifacts/lua/lua_strings_32.txt |
386,532 行 | 原始常量;内含协议 DSL 文本(req_/notify_ .. 操作码 { # 中文注释) |
Data/Raw/data/*.unity3d |
资源包清单 | 活动 UI / 横幅 / 加载 Spine 资源(ls | grep 得到) |
数据质量说明:LuaJIT 字符串池抽取存在定长截断,部分 token / 配置名首尾被截(如 req_→eq_/_,BackFestival_ActivityData→BackFestival_Acti,配置名常在 40 字符处截断)。故下文配置家族名按证据内可见形式引用,已被截断者加 … 或括号注明。所有结论均来自上述证据,无推测性内容。
1. 活动控制器清单(Activity Controller Inventory)#
活动系统的逻辑层统一挂在 game_module_logic_module_activity_ 命名空间下。顶层模块 token 块位于 tokens.txt:164797–165121。其架构为三件套:
| 层 | 类后缀 | 代表 |
|---|---|---|
| 控制器 | *C / *Ctrl |
ActivityC、ActivityGiftC、MineWarC |
| 数据 | *_ActivityData |
DivinationRoom_ActivityData、PinBall_ActivityData |
| 事件/日历 | activity_Event / eventcalendarc |
game_module_logic_module_activity_Event、..._eventcalendarc |
基础类型:ActivityDataBase、ActivityData、ActivityInfo、ActivityStage、ActivityState、ActivityType、ActivityGift/ActivityGiftInfo、ActivityDrop/DropEvent/DropEventId(见 tokens.txt 与 lua_strings_32.txt)。
下表为要求核对的活动控制器(均在 tokens.txt:164798–165119 命中),括号内为同名瞬时数据类:
| 控制器 | 数据类 | 一句话说明 | 关联配置家族(证据原文,截断照录) |
|---|---|---|---|
ActivityC |
ActivityDataB…/ActivityData |
活动总控/入口与红点,管理日历与开启状态 | game_config_default_activity_center_s…、..._activity_main_icon_…、..._activity_exchange_go… |
ActivityGiftC |
ActivityGiftInfo |
活动礼包(含触发/弹窗/自定义礼包)发放与领取 | game_config_default_activity_gift_po…、..._gift_activity_…、..._optional_gift_p…、..._birthday_gift_pre… |
ActivityDropC |
ActivityDrop_ActivityD…、DropEvent_Activity |
打怪/采集掉落活动(节日换皮:Children/Easter/Halloween/Valentine/DragonBoat/Lantern) | game_config_default_drop_activity_…;notify_activity_drop_direct 2103 |
DailyTaskC |
DailyTa…_ActivityD |
活动日常任务 + 节日战令日常 | game_config_default_fes_bp_dailytask…、..._daily_activity_t…/..._reward… |
AllianceTrapC |
AllianceTrap_ActivityD |
联盟巨兽陷阱(个人/联盟积分与排行奖励) | game_config_default_alliance_trap_c…、..._trap_damage_rew…、..._trap_strengt… |
BackFestivalC |
BackFestival_Acti… |
回归/回流活动(七日任务+积分+签到) | game_config_default_back_fivedays_task_c…/_m…/_rew… |
ChristmasFindC |
ChristmasFind_ActivityD |
圣诞寻宝类玩法控制器 | 未抽到独立 christmas_find_* 配置表;数据由协议下发 |
DivinationRoomC |
DivinationRoom_ActivityD |
占卜屋(设奖/开奖/记录) | 引导表 guide_weakguide_divination_…;玩法数据由协议下发 |
FireCrystalLabC |
FireCrystalLab_ActivityD |
火晶实验室(科技/周任务宝箱) | game_config_default_fire_crystal_man…、..._firecrystal_technology_c…、..._firecrystal_mission_r… |
FireCrystalLeakC |
FireCrystalLeak_ActivityD |
火晶泄漏事件(雷达/修复) | 引导 guide_weakguide_radar_fire_crystal_leak_… |
FrozenTreasureC |
FrozenTreasure_ActivityD |
冰冻宝箱(节日换皮主推玩法之一) | game_config_default_frozen_treasure_c…、..._lott…、..._rew… |
HuntTreasureC |
HuntTreasure_Activity… |
寻宝棋盘(格子/任务/兑换) | game_config_default_treasure_hunt_c…、..._chessbo…、..._rew… |
InteractiveC |
Interactive_ActivityD |
互动/装扮交互(周年互动等) | game_config_default_interactive_dress_con… |
LuckyCardC |
LuckyCard…(数据类被截断) |
幸运卡/抽卡 | game_config_default_fortune_card_r…/_choose_rew…/_extra_rew… |
MineWarC |
MineWa…_ActivityD |
矿战(报名/战斗/占领/排行奖励) | game_config_default_mine_war_gather_r…/_t…/..._rule_… |
PetFestivalC |
PetFestival_ActivityD |
宠物节(宠物玩法换皮活动) | game_config_default_pet_recharge_conf…、..._pet__recharge_c… |
PinBallC/PinBallD |
PinBall_ActivityD |
弹珠机(含儿童节换皮) | 引导 Guide_Pinball_Teach_Children202…;req_pin_game_task 20251 |
SevenFestivalC |
SevenFestival_ActivityD |
节日七日任务/积分 | game_config_default_festival_sevendays_c…/_rew…、..._sevendays_task_c… |
SocialMediaC |
(数据类未在证据中单独出现) | 社交/媒体分享类活动 | 无独立配置前缀命中;由协议下发 |
TrialOfSurvivalC |
HeroicTrials_ActivityD(推测同族) |
生存试炼 | game_config_default_heroic_trials_r…/_s… |
旁证:同目录另有大量非 activity 命名空间的活动控制器,功能等价:game_module_logic_module_activityaccumulative_AccumulativeActivityC(累计活动)、game_module_logic_module_anniversary_AnniversaryCtrl、game_module_logic_module_linkageharbin_LinkageHarbinC、game_module_logic_module_arsenalleague_*、game_module_logic_module_advertising_MirageTheatreC。
活动数据类全量(tokens.txt 截录,activity_data_ 后去前缀排序)共 110+ 个:Accumulative_ActivityD…、ActiveRecharge_ActivityD…、AnniversaryReview_ActivityD…、ArsenalLeague*、Balloon_ActivityD…、CapitalWar_ActivityD…、DuckMarket_ActivityD…、EmbarkOnANewJourne…、MergeMilestone_ActivityD…、PopularStar_ActivityD…、PitchPot_ActivityD…、ReturnSign_ActivityD…、Slg3League*、SnowClean_ActivityD…、StoveSkinGift_ActivityD…、SupremacyWar*、ThanksgivingCooking_ActivityD…、ThanksgivingFeast_ActivityD…、ThreeDayStore_ActivityD…、ThreeTiles_ActivityD…、TruckEscort_ActivityD…、Turntable_ActivityD…、ValueExchange_ActivityD…、VersionUp_ActivityD…、WorldBoss_ActivityD… 等。
2. 季节 / 节日活动战役(Seasonal & Holiday Campaigns)#
节日活动由同一批通用子玩法(PassCheck 通行证、FrozenTreasure、MostLords、PitchPot、SlotMatrix、FestivalTurntable、Accumulative、ValueExchange、PinBall、ThreeTiles、Recharge、SignIn…)叠加 活动前缀 组合而成。证据中可确定到年的 campaign 如下(token 大小写不敏感,均取自 tokens.txt / lua_strings_32.txt / Raw/data 清单):
| 战役 | 关键 token / 类 | UI 面板 bundle(节选) | 横幅/加载资源 | i18n/配置佐证 |
|---|---|---|---|---|
| 端午 DragonBoat 2026(及 2024/2025) | DragonBoat2026*、DragonBoat2025*、DragonBoatFestival2025、CreateDragonBoatFestival2025Tree |
ui_panel_activity_drop_activitydropdragonboatfestival2024/2025subpanel、DragonBoat2026PassCheckAndDailyTask*、DragonBoat2026SlotMatrix*、DragonBoat2026Exch…、DragonBoat2026PitchPot* |
spine_ui_loading_dragonboat2026、art_spine_ui_banner_dragonboat2025_gift_01/02_anim |
词条 dragonboat2026_exchangeshop_single_bg_4…、SLOTMATRIX_NUMBER_STATS_DragonBoat2026 |
| 世界杯 2026 World Cup | Activity_WorldCup2026、CreateActivityWorldCup2026Tree、WorldCup2026Pumpkin、worldcup2026_passcheck_icon |
worldcup2026_*、…PassCheck…、folt 系列 |
spine_ui_loading_worldcup2026_01..04_anim、spine_ui_banner_worldcup2026_gift_anim |
worldcup2026_festival_turntable_gift/rule/reward/bg_20x、头像 art_spine_ui_icon_touxiang_worldcup2026_anim |
| 中秋 2026 Mid-Autumn(及 2024/2025) | Activity_MidAutumn、Activity_MidAutumn2025/2026、CreateActivityMidAutumn2026Tree、Midautumn2026* |
ui_panel_frozentreasure_midautumn2024frozentreasure*、ui_panel_midautumn2024_midautumn2024popuppanel、ui_panel_accumulativeactivity_midautumnaccumulativeactivitysubpanel |
spine_ui_banner_mid_autumn2026_anim、scene_pitchpot_activity_pitchpot_scene_midautumn2026 |
中文串「【中秋的祈愿】活动即将结束/将于%{time}结束」、Midautumn2026PassCheckAndDailyTask |
| 少儿/儿童节 2026 Children's Day | Children2026*、Childrensday2026PassCheckAndDailyTask*、Children2026SevenDays、PinBallChildren2026、CreateActivityChildren2026Tree |
ui_panel_mostlords_childrensdaymostlords*、ui_panel_childrenfestival_childrenfestivalpanel |
art_spine_loading_loading_childrenday2026_01/02_anim、spine_ui_banner_children2026_giftpack_anim、spine_ui_banner_children2026_signin_anim |
中文串「儿童节奇趣礼包」「儿童节盛宴筹备」「可获得儿童节个人派对积分」 |
| 元宵 Lantern 2025 / 2026 | Activity_LanternFestival2026、CreateLanternFestival2026Tree、Lantern2026PassCheckAndDailyTask* |
ui_panel_activity_drop_activitydroplantern2025subpanel、lanternfestival2026_slotmatrix*、lanternfestival2026_single_valueexchange* |
art_spine_ui_banner_lanterngift2025_anim、spine_ui_lanternfestival_drawlots_2026_anim |
中文串「糯糯元宵铭牌」 |
| 周年庆 Anniversary(2024/2025/2026 + 半周年) | Anniversary2025/2026、AnniversaryReview2026、AnniversarySignIn…、CreateAnniversary2025Tree、CreateAnniversaryReviewTree、onehalfanniversary |
ui_panel_anniversarysignin_*、ui_panel_interactive_anniversaryinteractive*、ui_panel_main_sub_lobbycenteranniversaryfeastitem |
art_effect_ui_material_anniversary2026、scene_activity_anniversarysigndraw_fxui…、art_effect_ui_material_onehalfanniversary |
中文串「周年信件」「事件一周年」、game_config_default_anniversary_*(mess/pv/review/tower…) |
| 感恩节 Thanksgiving(2024/2025) | Thanksgiving2024/2025、CreateActivityThanksGivingTree、ThanksgivingFeast*、ThanksgivingCooking* |
ui_panel_main_sub_lobbycenterthanksgivingfeastitem、ui_panel_frozentreasure_* |
ui_panel_worldscene_thanksgiving_fxui_*、art_spine_ui_icon_touxiang_thanksgiving2025_anim |
中文串「感恩节盛宴」;配置 thanksgiving_feast_*、thanksgiving_cook_*、thanksgiving_menu_*、thanksgivingday_* |
| 圣诞 Christmas(2023/2024/2025) | CreateActivityChristmas2024Tree、Christmas2024*、christmas2024frozentreasure*、ChristmasFindC、ChristmasFind_ActivityD |
ui_panel_threetiles_christmasthreetiles*、ui_panel_accumulativeactivity_christmasaccumulativeactivitysubpanel、scene_city_christmas_npc_xmas |
art_effect_ui_material_christmas2023、ui_panel_lobby_fxui_lobby_christmas_tishi_wwj |
中文串「装扮圣诞树」「【水晶圣诞树】装扮」「圣诞…」 |
| 哈尔滨联动 Linkage Harbin(冰雪大世界) | LinkageHarbinC、LinkageHarbin_ActivityData、linkageharbin_activityd、CreateLinkageHarbinNode |
ui_panel_linkageharbin_linkageharbinsubpanel |
词条 linkage_harbin_ui_01..08 |
定义文件 sharedef/linkage_harbin_activity_def.lua(协议注释原文:「奖励状态(取值见 sharedef/linkage_harbin_activi…」lua_strings_32.txt);中文串「冰雪大世界」;err_linkage_harbin_received;banner activity_banner_20023001 |
| 足球 Football = 世界杯 2026 | 同上 WorldCup2026 |
同上 | 同上 | 头像/横幅/加载三件套均以 worldcup2026 命名 |
| (佐证)音乐节 2025、运动会 2025、联动大富翁 2026 | MusicFestival2025、CreateActivityMusicFestival2025Tree、SPORTS_MEET_2025_FESTIVAL_TURNTABLE_、CreateActivitySportsMeetTree、LinkageMonopoly2026PassCheckAndDailyTask |
ui_panel_lobbycenter…musicfestivalfeastitem、ui_panel_travel_monopoly_*、ui_panel_activitygift_activitygiftlinkagemonopolyslapface* |
spine_ui_banner_music2025_01..03_anim |
game_config_default_monopoly_linkage_*(联动大富翁配置族) |
说明:DragonBoat2026、WorldCup2026、Children2026、Midautumn2026、Lantern2026、Anniversary2026、Valentine2026、Spring2026(春节)在 token 池中出现的换皮词条数量为 60–109 个/战役,说明活动以「通用玩法模板 + 节日皮肤」批量复用(见 §3)。
3. 活动定义与生命周期(Data-Driven Activity Model)#
3.1 定义文件与配置表
- 活动定义文件:证据中唯一命名的服务端定义文件是
sharedef/linkage_harbin_activity_def.lua(lua_strings_32.txt协议注释引用),命名模式为sharedef/<活动>_activity_def.lua;其字段被协议注释直接引用(「奖励状态(取值见 sharedef/…_activity_def.lua)」)。 - 通用活动数据模型(协议 DSL 字段,
lua_strings_32.txt):id、activity_type、begin_ts/end_ts、actEndTime、actSettleTime、start_time/end_time、main_icon_id、calendar_color、calendar_bg、calendar_desc、banner、preview_banner、activity_name、activity_icon、activityRandomKey、map_id、boxRewardList、illusts。→ 说明活动开启/结束时间、图标、日历配色、横幅、随机种子均由服务端配置下发(数据驱动),客户端仅渲染。 - 核心配置家族(
config_tables.txt,截断照录):
| 家族 | 代表表名(证据原文) | 用途 |
|---|---|---|
| 充值/累计 | game_config_default_active_recharge_activity_t…、..._active_recharge_coll…、..._accumulative_recha…、..._month_recharge_c… |
充值类活动条件与档位 |
| 活动中心/图标 | ..._activity_center_s…、..._activity_main_icon_…、..._activity_main_icon_lo…、..._activity_exchange_go… |
活动日历、入口图标、兑换跳转 |
| 目标/积分 | ..._target_activity_c…、..._integral_activity_c…、..._target_rank_rew… |
目标积分活动与排行奖励 |
| 礼包 | ..._activity_gift_po…、..._gift_activity_…、..._optional_gift_p…、..._birthday_gift_pre…、..._subscription_gift_con… |
活动礼包/可选礼包/生日礼包/订阅 |
| 掉落 | ..._drop_activity_…、..._relic_adventure_drop_rew… |
掉落活动 |
| 抽奖/转盘 | ..._recharge_lottery_…、..._recharge_lottery_guarant…(保底)、..._recharge_lottery_rew…、..._hero_turntable_r…、..._festival_turntable_gr…/_rew…/_s…、..._icebound_amazing_lottery…、..._fortune_card_* |
抽奖/转盘/幸运卡 |
| 排行 | ..._world_boss_rank_rew…、..._three_tiles_rank_rew…、..._fishing_ranking_*、..._arsenal_league_rank_rew…、..._popular_star_rank_rew… |
活动排行榜奖励 |
| 节日七日 | ..._festival_sevendays_c…/_rew…、..._sevendays_task_c…/_con…/_group/_rew… |
七日任务/节日挑战 |
3.2 Gift / Drop / Rank / Lottery 四大子类型
| 类型 | 逻辑类 | 数据/协议 | 配置 |
|---|---|---|---|
| Gift(礼包) | ActivityGiftC/ActivityGiftCtrl;prefab_free/prefab_custom(触发礼包面板绑定) |
ActivityGiftInfo;req_activity_gift_popup_claim 21131、notify_pick_one_gift_info 17601、push_multiple_choice_activity_info 16410 |
activity_gift_po…、gift_activity_…、gift_type 字段 |
| Drop(掉落) | ActivityDropC/ActivityDropCtrl;ActivityDrop{Common,Children,Easter,Halloween,Valentine,…}SubPanel |
DropEvent/DropEventId/DropEvent_Activity/ActivityDrop_ActivityD…;notify_activity_drop_direct 2103、NotifyDropEventInfo |
drop_activity_…、drop_type 字段 |
| Rank(排行) | 各活动 *RankPanel;服务端排行 |
req_target_integral_rank 10252、req_world_boss_get_rank 20612、req_three_tiles_get_rank_info 15783、req_popular_star_rank 20501、req_fishing_self_rank 13512 |
target_rank_rew…、three_tiles_rank_rew…、world_boss_rank_rew… |
| Lottery(抽奖/转盘) | 转盘/抽奖面板 | req_recharge_lottery_draw 20281、req_sign_draw_lottery 16132、req_hero_turntable_get_free_gift 10353、req_festival_turntable_shop_buy 20645、LOTTERY_DRAW/FREE_SUCCESS 事件 |
recharge_lottery_guarant…(保底机制)、festival_turntable_*、hero_turntable_* |
3.3 生命周期(日历 → 开启 → 进行 → 结算 → 关闭)
证据中的状态机与消息:ACTIVITY_STATE、ACTIVITY_STATE_CHANGE、ACTIVITY_CHANGE_STAGE、ACTIVITY_ENTER、ACTIVITY_FORCE_CLOSE、ACTIVITY_CLOSE、ACTIVITY_END、ACTIVITY_END_TIME、CheckActivityState、ActivityStage。
| 阶段 | 关键协议 / 操作码 |
|---|---|
| 拉取日历 | req_activity_calendar_get_data 9901 → notify_activity_calendar_data 9925 |
| 活动信息/阶段 | notify_test_activity_info 2101、notify_version_up_activity 13161、notify_activity_calendar_data |
| 开启/关闭(调试) | start_activity 1234、close_activity 1235、req_cheat_refresh_activity_event 1278、req_cheat_open_global_activity 14008 |
| 掉落推送 | notify_activity_drop_direct 2103 |
| 入口/报名 | req_alliance_activity_auto_join_info 10001 / _open 10002、notify_fortwar_activity_init 10761、req_linkage_monopoly_open_activity 30758 |
| 结束 | ACTIVITY_FORCE_CLOSE、ACTIVITY_GIFT_END |
4. 协议(Protocol / Opcodes)#
活动相关操作码分布(protocol_ops.txt + lua_strings_32.txt 交叉核对;部分 req_ 因截断显示为 eq_/q_/_):
| 操作码 | 协议名 | 分类 |
|---|---|---|
| 9901 | req_activity_calendar_get_data |
活动总控/日历 |
| 9925 | notify_activity_calendar_data |
活动总控/日历 |
| 2101 | notify_test_activity_info |
活动总控 |
| 2103 | notify_activity_drop_direct |
掉落 |
| 1234 / 1235 | start_activity / close_activity |
生命周期(作弊/调试) |
| 21131 | req_activity_gift_popup_claim |
礼包 |
| 17601 / 17602 | notify_pick_one_gift_info / notify_pick_one_gift_recharge_success |
礼包(多选一) |
| 16410 | push_multiple_choice_activity_info |
礼包(多选一) |
| 16151 | req_gift_message_get_reward |
礼包 |
| 9751 / 9770 / 9772 | req_accumulative_collect_reward / notify_accumulative_info / notify_accumulative_progress_change |
累计充值/消耗 |
| 12921 / 12931 / 12932 / 12933 | req_activity_get_task_reward / …task_activity_info / …activity_task_update / …activity_integral_update |
活动任务/积分 |
| 12922 | req_daily_task_activity_get_integral_reward |
日常任务活动 |
| 11301 / 11352 | notify_exchange_activity_info / req_exchange_activity_exchange_all |
兑换活动 |
| 20645 / 20647 | req_festival_turntable_shop_buy / notify_festival_turntable_info |
节日转盘 |
| 10352 / 10353 / 10354 | req_hero_turntable_extra_reward / _get_free_gift / _switch_version |
英雄转盘 |
| 20281 / 20282 / 20291 | req_recharge_lottery_draw / _acc_box / notify_recharge_lottery_info |
充值抽奖 |
| 16131 / 16132 / 16133 / 16140 | …sign_draw_get_sign_progress_reward / req_sign_draw_lottery / …free / notify_sign_draw_info |
签到抽奖 |
| 11551 / 11552 / 11757 | req_get_festival_sevendays_quest_reward / _score_reward / update_festival_sevendays_quest |
节日七日 |
| 20261 / 20262 / 20271 / 20272 | req_get_sevenday_task_reward / _score_reward / notify_sevenday_task_init / notify_update_sevenday_task_quest |
七日任务 |
| 20250 / 20251 | notify_pin_game_update / req_pin_game_task |
弹珠机 |
| 20423 | req_pachinko_task |
弹珠/扭蛋 |
| 20751 / 20757 | req_balloon_sign_reward / notify_balloon_sign |
气球签到 |
| 16252 / 16253 / 16261 | req_home_sign_get_reward / _all_reward / notify_home_sign_info |
家园签到 |
| 15814 / 15783 | notify_three_tiles_task_info / req_three_tiles_get_rank_info |
三消/翻牌 |
| 17410 / 17411 | notify_puzzle_shop_info / req_puzzle_shop_buy_item |
拼图商店 |
| 21031 / 21033 | notify_magic_potion_init / req_magic_potion_task_reward |
魔法药水 |
| 20926 / 20927 / 20903 | notify_pitch_pot_ticket / _task_update / req_pitch_pot_task_reward |
投壶 |
| 15670 / 15672 / 15601 / 15622 / 15623 | notify_thanksgiving_cooking_info / …exchange_lst_nums_update / req_thanksgiving_sign_get_reward / req_thanksgiving_menu_rank / …self_rank |
感恩节 |
| 15635 / 15655 / 15683 | …thanksgiving_menu_activity_info / req_thanksgiving_cooking_exchange / req_thanksgiving_bp_alliance_member_rank |
感恩节 |
| 11652 / 11654 / 11655 / 11683 / 11684 | req_fes_bp_get_all_reward / _donate_item / _rank / notify_fes_bp_purchase / _buy_gift |
节日战令 |
| 13753 / 13754 / 13782 / 13785 | req_fes_bp_dailytask_get_extra_reward / _task_reward / notify_fes_bp_dailytask_purchase / …task_info |
节日战令日常 |
| 10406 / 10408 / 10410 | req_alliance_trap_join_count / _batch_get_personal_score_reward / _auto_reserve |
联盟陷阱 |
| 16815 / 16816 / 16856 | notify_supremacy_war_feast_gift_buy / …unread_record / …supremacy_war_gift_record |
霸主盛宴 |
| 16571 / 16581 | req_supremacy_war_task_reward / notify_supremacy_war_task_info |
霸主之战 |
| 20614 / 20632 | req_world_boss_get_boss_info / notify_world_boss_info_update |
世界 BOSS |
| 30201 / 30202 | req_linkage_harbin_change_state / req_linkage_harbin_get_reward |
哈尔滨联动 |
| 30745–30790 | req_linkage_monopoly_* / notify_linkage_monopoly_*(约 20 条:open_activity 30758、free_gift 30769、play_dice 30786、quest 30789/30790…) |
联动大富翁 |
| 4352 / 4652 / 7431 / 11005 / 13507 / 16292 / 30702 | req_daily_bundle_get_free_gift / req_vip_get_daily_free_gift / req_gift_buy_free_gift / req_fortune_card_get_daily_free_gift / req_fishing_get_free_gift / req_limit_time_shop_get_free_gift / req_stove_skin_gift_daily_free_reward |
每日免费礼包 |
| 10908 | req_capital_war_gift |
王城战礼包 |
| 14003–14016 | req_cheat_back_start / …back_end / …finish_festival_sevenday_quest / …thanksgiving_menu_add_integral 等 |
活动作弊面 |
另有世界/联盟情境活动(req_situation_activity 7058、req_unwatch_alliance_situation_activity 7007)、活动战力同步(notify_player_activity_power_update 1030)、动员活动更新(notify_mobilization_activity_update 10172)、版本升级活动(notify_version_up_activity 13161)。
通知/推送类活动 op 还包含:notify_activity_drop_direct 2103、notify_activity_calendar_data 9925、notify_festival_turntable_info 20647、notify_recharge_lottery_info 20291、notify_sign_draw_info 16140、notify_accumulative_info 9770、notify_fes_bp_info 11681、notify_thanksgiving_cooking_info 15670、push_multiple_choice_activity_info 16410。
5. 风险(Risk)#
5.1 限时优惠 / FOMO 推送
- 客户端内置大量触发式礼包面板:
ui_panel_gift_giftpushcustompanel_100001..2026、gifttriggerbagpanel_2026、gifttriggerbagpanel_2490050/2490356/2490359/…、gifttriggerbagbirthdaypanel、gifttriggeralonepanel、giftrechargetokenpanel、giftpushcustomtriggerpanel、pushmultiplechoicepackpanel_100001..100007、vipexclusivegiftpackagesubpanel、optionalgiftpanel。 - 配合限时商店与每日免费礼包协议(
req_limit_time_shop_get_free_gift 16292、req_daily_bundle_get_free_gift 4352、req_vip_get_daily_free_gift 4652、req_stove_skin_gift_daily_free_reward 30702),构成「倒计时 + 触发弹窗 + 每日免费领取」的经典 FOMO(错失恐惧) 商业化链路。 - 累计充值/消耗(
accumulative)、节日战令(fes_bp)、转盘(turntable)均在活动截止时清零/结算(ACTIVITY_END_TIME、ACTIVITY_FORCE_CLOSE)。
5.2 抽奖 / 扭蛋概率
- 存在保底配置
game_config_default_recharge_lottery_guarant…,说明保底机制为服务端配置驱动;抽奖/转盘还包括hero_turntable_*、festival_turntable_*、sign_draw_lottery、fortune_card_*、treasure_hunt_*、icebound_amazing_lottery。 - 协议字段含
activityRandomKey(随机密钥/种子),抽奖结果由服务端裁定(LOTTERY_DRAW_SUCCESS等事件);赔率/保底不进客户端,但 UI 会展示「概率/说明」文本(recharge_ui_22、festival_turntable_err_01「活动数据异常,请稍后再试」)。 - 风险点:多入口(充值抽奖 20281、签到抽奖 16132、英雄转盘 10353、节日转盘 20645)并存的概率型付费,需关注各地合规披露(概率公示、保底声明)。
5.3 客户端信任边界
- 业务逻辑整体位于 LuaJIT 字节码(可逆),活动状态机(
ActivityState/ActivityStage)、掉落展示、礼包触发面板均在客户端执行;服务端通过req_/notify_协议做权威校验(领取、购买、抽奖均走req_*)。 - 证据中存在成体系的作弊接口(
req_cheat_*:req_cheat_open_global_activity 14008、req_cheat_refresh_activity_event 1278、req_cheat_start_sevenday_task 14062、req_cheat_fes_bp_add_integral 1280、req_cheat_thanksgiving_menu_add_integral 14016、req_cheat_linkage_monopoly_try_join_teams 14300等)。这些接口是攻击面:若服务端鉴权不足,可能被用于刷活动积分/奖励;建议确认其在正式服被禁用或严格白名单。 - 活动定义以
sharedef/<活动>_activity_def.lua(如linkage_harbin_activity_def.lua)形式下发,配置/奖励判定若被客户端缓存,需验证奖励领取是否二次校验而非仅信任客户端上报。
证据复核要点:活动控制器 tokens.txt:164798–165119;活动数据类 110+ 条;协议操作码取自 protocol_ops.txt 与 lua_strings_32.txt 协议 DSL;节日 campaign 词条来自 tokens.txt / Raw/data 资源清单;sharedef/linkage_harbin_activity_def.lua 为 lua_strings_32.txt 中唯一显式命名的活动定义文件。
附录 · 复现环境、命令与artifacts#
A.1 分析环境#
| 项 | 值 |
|---|---|
| 主机 | macOS 26.6.2 (Build 25G83), arm64 (Apple Silicon) |
| 工具链 | Xcode Command Line Tools(otool/nm/lipo/codesign/plutil/dyld_info/unzip/strings) |
| 脚本 | Python 3.9.6 + numpy 2.0.2 |
| 样本 SHA-256 | 1e64c47197def718e28f5ed0acd4aeba03bcfe497bf6b3284ff086d060a8b3aa |
A.2 关键产物(playground20261012/artifacts/)#
| 文件 | 说明 |
|---|---|
global-metadata.dec.dat |
解密后的 IL2CPP 元数据(9,902,340 B, version 31) |
out_identifiers.txt |
从元数据还原的 88,084 个标识符(类/方法/字段名) |
out_literals.txt |
19,294 条字符串字面量(含 URL/密钥线索) |
out_types.txt |
按程序集分组的 8,511 个类型全名 |
key612.bin / key612_mode.bin |
众数法恢复的候选密钥(过程产物) |
decrypt_global_metadata.py |
可复现的元数据解密脚本 |
A.3 一键复现#
# 0) 解包
cd /Users/jarvis/Downloads/playground20261012
unzip -q com.gof.china_1.15.6_und3fined.ipa -d extracted
APP=extracted/Payload/whiteoutsurvivalcn.app
# 1) 确认脱壳
otool -l $APP/whiteoutsurvivalcn | awk '/LC_ENCRYPTION_INFO_64/,/cryptid/'
otool -l $APP/Frameworks/UnityFramework.framework/UnityFramework | awk '/LC_ENCRYPTION_INFO_64/,/cryptid/'
# 2) 解密 IL2CPP 元数据(密钥 NTESCSLLVMPASSKEY,周期 17)
python3 artifacts/decrypt_global_metadata.py \
$APP/Data/Managed/Metadata/global-metadata.dat \
artifacts/global-metadata.dec.dat
# 3) 校验
xxd artifacts/global-metadata.dec.dat | head -1
strings artifacts/global-metadata.dec.dat | grep -m3 -E 'AntiCheatResult|DecryptLuaBytecode|UnityEngine.MonoBehaviour'
A.4 元数据解析片段(自研解析器)#
import struct
d = open("artifacts/global-metadata.dec.dat", "rb").read()
u32 = lambda o: struct.unpack_from("<I", d, o)[0]
assert u32(0) == 0xFAB11BAF and u32(4) == 31
# 头字段: magic, version, (offset,size) * N —— 见报告 6.3.1 表
off_str, cnt_str = 0xAB648, 0x211DDC
heap = d[off_str:off_str+cnt_str]
def name(i):
return heap[i:heap.find(b"\x00", i)].decode("utf-8", "replace")
# 类型定义 size=88, 字段 nameIndex 在偏移 0, namespaceIndex 在偏移 4
td, SZ = 0x81394C, 88
for i in range(748968 // SZ):
b = td + i*SZ
print(name(u32(b)), name(u32(b+4)))
A.5 主要命令清单#
# 结构
file whiteoutsurvivalcn; lipo -info whiteoutsurvivalcn
otool -hv whiteoutsurvivalcn
otool -L Frameworks/UnityFramework.framework/UnityFramework
# 权限 / 签名
codesign -dv --verbose=4 whiteoutsurvivalcn
codesign -d --entitlements :- whiteoutsurvivalcn
plutil -p Info.plist
plutil -p PlugIns/CGPushServerExtension.appex/Info.plist
# 串
strings -n 6 Frameworks/UnityFramework.framework/UnityFramework | grep -iE 'jailb|cydia|frida|AntiCheat|tamper'
# 框架版本
for d in Frameworks/*/; do \
echo "$d $(plutil -extract CFBundleShortVersionString raw "$d/Info.plist")"; done
A.6 游戏内容(Lua)提取工具链#
包内玩法逻辑以 LuaJIT 字节码 形式承载于 luapackage_32/64(各 406 个压缩块,首个块加密)。提取步骤:
# 1) 重建解压流(跳过加密的首块)
python3 tools/extract_lua.py # -> artifacts/lua/*.partial/bin
# 2) 从字节码中提取字符串常量(ULEB128 长度前缀启发式)
python3 tools/extract_lua_strings.py artifacts/lua/luapackage_32_*.partial.bin artifacts/lua/lua_strings_32.txt
产出数据集(artifacts/lua/):
| 文件 | 内容 |
|---|---|
luapackage_32/64_*.partial.bin |
重建数据流(各 ~53MB) |
lua_strings_32.txt |
从字节码提取的 297,209 条字符串常量 |
tokens.txt |
252,027 个干净标识符 |
config_tables.txt |
1,600 个 game_config_* 配置表名 |
protocol_ops.txt |
2,635 条网络协议 opcode ⇄ 名称 |
ui_views.txt |
6,977 个 View/Panel/Ctrl |
activity_tokens.txt |
129 个活动/节日标识符 |
说明:luapackage 中内嵌了游戏自己的网络协议定义文本(形如 _league_detail_4_player_situation 17208 { request { owner 0 : ... } }),据此可完整还原 opcode 表与字段结构。
A.7 报告合并与站点构建#
python3 tools/merge_report.py # 合并 report/*.md -> 单一全文
python3 tools/build_site.py # 生成 site/(index.html + 样式 + 交互)
npx wrangler pages deploy ./site --project-name=whiteout-survival-reverse --branch=main
A.8 免责声明#
以上全部操作均为离线、只读的静态分析,未连接任何线上服务、未绕过鉴权与付费。仅用于授权范围内的安全研究与防御加固。