要把远程热更新功能给去掉,删除原生相关的热更新引擎代码。

上面已经把原因、本质说得很清楚了吧,你按着思路来就行了。只要有远程加载可执行脚本那个能力链的,苹果风控模型基本都会5.6先拦截,然后再等你沟通申诉,再决定是否放行,因为风险确实太大了。站着平台的角度,这样的拦截确实是很有必要的,因为审核通过后,你完全可以通过远程下发代码,把App换成其他任何形式,包括违规,违法那些。你可以先沟通,他通过后会叫你重新提交一份新的二进制的。沟通后不放行,你就得按上面的思路裁剪原生代码了,把用不到的裁掉,特别是文中提到的远程加载可执行脚本能力链。

热更新确实是最直观的表现形式,更加本质地说是: App 如果具备在审核后通过远程下发解析型代码,把审核时的行为 A 变成实际运行时的行为 B 的能力,甚至完全变成另一个App。所以具备这样的能力链的都需要注意,按现在的反馈来看,基本上都会5.6先拦的,尽管沟通后有可能放行,但是来回提交,沟通很浪费时间,建议上架前自查或者业务设计阶段就要注意。

20260914
大家好,更新一下之前的问题。

我第一次提交一款 iOS 单机买断游戏,只计划在海外 App Store 发布,不包含国区。前后几次提交中,两次在进入 In Review(审核中) 后几乎立即被 Guideline 5.6 - Developer Code of Conduct(开发者行为准则) 拒绝。

Apple 的理由是:

97e79b5a-9165-4d6b-8b80-90bfc5cddcfb
97e79b5a-9165-4d6b-8b80-90bfc5cddcfb
2528×634 108 KB
We’ve identified a pattern of unusual behavior with the app that is commonly associated with fraudulent activity. Specifically, the app contains features that appear to have been intentionally hidden during the review process.

中文大意:

我们发现该 App 存在一种异常行为模式,这类行为通常与欺诈活动相关。具体来说,该 App 包含一些看起来像是在审核过程中被有意隐藏的功能。

我的游戏本身是纯单机买断制,没有账号、IAP(App 内购买)、广告、订阅,也没有业务上设计的远程下发功能。

第一次收到 5.6 后,我一开始也以为是业务层面存在某些“看起来像隐藏功能”的逻辑,所以把未使用的开发 / 测试功能、未发布功能、旧逻辑、WebView 以及一些无关的跨平台残留都尽量清理,同时补充了详细的 Review Notes(审核备注)和真机录屏。

但第二次提交后,仍然在进入 In Review(审核中) 后几乎立即收到完全相同的 5.6。

由于 Release 业务脚本本身还是加密后的 .jsc,自动风控很难直接理解完整业务逻辑,因此这种“秒拒”更像是由最终二进制中的静态特征、模块能力或调用链触发,而真正的业务层隐藏功能更依赖人工运行和判断。

第二次依然几乎5.6秒拒后,我请求 App Review(App 审核团队)进一步说明,并提交 Appeal(申诉)。过了约26小时后,Apple 回复:

We have determined that the App Review Guideline 5.6 issue that was previously identified is addressed.

中文:

我们已经确认,之前识别出的 App Review Guideline 5.6 问题已经得到解决。

也就是说,这次 5.6 最终是通过沟通和复核解决的。

这次经历下来,我觉得遇到 5.6 这种性质比较严重、但拒绝信息又非常模糊的问题时,沟通和人工复核其实是最主要的解决渠道。

之前还在帖子里面担心问多几次会不会封号。至少从我这次的实际经历看,没有必要因为这种担忧而不敢继续沟通。正常说明业务、补充审核信息、请求明确问题、请求人工复核,本来就是 App Review 提供给开发者的正常渠道。

真正有风险的不是“问了几次”,而是明知存在违规行为,仍然反复提交、故意隐瞒、误导审核,或者试图绕过审核机制。

所以如果自己确认业务没有主观隐藏、欺骗或规避审核的问题,遇到这种模糊的 5.6,与其不断盲目删代码、重新提交,不如把产品用途、商业模式、功能入口、网络行为等说明清楚,必要时继续通过 App Review 沟通和 Appeal(申诉)请求复核。

Apple 并没有明确告诉我到底是哪一个具体模块触发了 5.6。不过在继续排查最终 iOS 二进制时,我发现了一个更值得注意的问题。

我对实际提交的 Archive(归档包)主程序做了 strings 等静态检查,最终二进制里能直接看到的明文内容主要集中在 cocos2d-x 引擎、JSB 和 V8 运行时这一层,在二进制中直接看到了大量仍然保留的明文类名、函数名和接口,例如:

AssetsManagerEx
loadRemoteManifest
checkUpdate
downloadFailedAssets
Downloader / DownloadTask / DownloaderApple
XMLHttpRequest
HttpClient / HttpRequest
WebSocket / SRWebSocket
NSURLSession
FileUtils
ScriptEngine
runScript / evalString
这些东西并不是简单几个无意义字符串,而是能看出网络获取、下载、本地文件处理、脚本加载和执行等能力都存在于最终构建中。

虽然最终提交的是已经编译后的二进制,但二进制并不意味着所有内容都不可读。类名、函数名、Objective-C selector(Objective-C 方法选择器)、日志、错误信息以及部分符号信息,仍然可能以明文形式保留在 Mach-O 中,因此可以通过 strings、otool、nm 等工具辅助判断最终构建实际包含了哪些能力。

考虑到这些关键类名、函数名和接口在二进制中本身就是可见的,再结合我这几次进入 In Review(审核中) 后几乎立即触发 5.6 的表现,我认为 Apple 的自动风控大概率也会把这类字符串、符号、模块特征以及相关调用关系作为静态分析的重要输入之一,再结合其他信号判断风险。

这些能力组合起来以后,客观上可以形成类似:

网络获取 / 下载
→ 写入本地
→ 加载脚本
→ ScriptEngine / V8 解析执行

这样的能力链。

本质就是:

App 如果具备在审核后通过远程下发解析型代码,把审核时的行为 A 变成实际运行时的行为 B 的能力,就存在绕过 App Review 的高风险。

这种风险不在于“有没有隐藏某一个按钮”,而在于它可能让审核本身失去约束力:Apple 审核的是 A,但用户最终运行的却可能通过审核后的远程代码变成 B。

从平台角度看,这已经不是普通的功能披露问题,而是 App 在通过审核后仍然保留了一条重新改变核心行为的通道。一旦被有意利用,就可能绕过功能审核、内容审核、商业模式审核以及其他合规要求。

这也就比较能解释为什么此类问题一旦被判断为与规避审核有关,会被上升到 Guideline 5.6 - Developer Code of Conduct(开发者行为准则),并出现 fraudulent activity(欺诈性活动)、intentionally hidden(有意隐藏) 这样相对严重的措辞。

这类风险并不是 Cocos Creator 独有的。

只要技术栈同时具备:

远程获取新的可执行脚本
+
本地解析、执行脚本的运行时

就应该检查是否客观存在完整的远程代码下发执行链。

例如:

Cocos Creator 或其他 JavaScript 游戏引擎
HTML / H5 / WebView
React Native
Lua / JavaScript 等脚本运行时
自行嵌入 V8、JavaScriptCore、QuickJS 等解释型运行环境的 App
问题并不是“用了 JavaScript 就有风险”,而是这些能力组合起来以后,是否真的能够在审核之后远程改变 App 的核心行为。

在我使用的 Cocos Creator 2.4.5 中,使用默认 cocos2d-x native engine(原生引擎)构建时,这些相关模块会随着默认引擎一起进入最终二进制,并不取决于业务代码有没有主动调用。

也就是说,即使我的单机游戏业务上完全不需要这些能力,最终提交的二进制里仍然客观保留了这一整套能力基础。

这次 5.6 最终已经通过沟通和复核解决,但审核问题解决,并不代表技术风险就不存在。

所以后来我还是借助 AI 辅助修改了一份 Custom Native Engine(自定义原生引擎),把自己项目完全不需要的相关模块从最终 Release(发布版)中剔除了。

我这次能够通过沟通解除 5.6,可能也和 App 本身是单机买断游戏、没有账号、IAP、广告、远程业务、服务器下发功能,整体产品形态比较简单有关。审核方更容易结合实际业务判断,并不存在主观隐藏功能或规避审核的场景。

但如果是一个本身高度依赖网络、远程内容、动态配置的 App,使用了类似技术架构,同时又客观存在:

服务器下发可执行脚本
→ App 本地加载
→ 运行时执行

这样的完整调用链,就未必那么容易解释清楚。

所以为了避免类似 5.6 风险,我更建议开发者从业务设计阶段就谨慎对待:

远程下发代码、远程替换核心脚本、可执行逻辑热更新,以及任何审核后可以改变核心行为的机制。

这些能力属于非常敏感的审核风险点。轻则可能导致反复拒审和人工复核,重则如果平台认为存在故意规避审核、欺骗审核或持续违规,也可能进一步影响 App 的发布资格,甚至开发者账号。

如果项目确实需要联网、资源下载或者动态内容,也不意味着这些能力都必须删除。

真正应该检查的是实际调用链:

是否客观存在“服务器下发新的可执行代码 → App 本地加载 → 运行时执行 → 改变审核后核心行为”的逻辑。

如果只是下载普通数据、图片、关卡配置等非可执行内容,和远程下发代码并执行不是一回事。

另外,也比较希望 Cocos 官方以后能够进一步加强引擎模块的解耦。

像 Downloader(下载器)、HTTP/XHR、WebSocket、AssetsManagerEx、WebView、动态更新等能力,最好能够在构建阶段真正做到模块化、可选编译,由开发者明确决定哪些能力需要进入最终 Release:

需要什么就编译什么,不需要的模块就不要进入最终二进制。

这样不仅能减小包体和攻击面,也能让开发者更清楚地控制最终 App 实际具备的能力边界,而不是为了移除项目完全用不到的功能,还需要自己修改底层 cocos2d-x、JSB 或 runtime(运行时)。

对于准备上架 App Store 的项目,我觉得这种“最小能力构建”非常重要。

最后建议大家准备提交 iOS 时,不要只检查自己的业务代码,也最好看一下最终 Archive 中真正提交给 Apple 的二进制。

重点不是“有没有某一个关键词”,而是:

这些能力组合起来以后,到底能不能让 App 在审核后从 A 变成 B。

这次 5.6 最终已经解决,希望这次排查经验能给其他使用脚本运行时、跨平台框架或者类似技术栈上架 iOS 的开发者一些参考。

标签: none

添加新评论