Cocos2d-xiOS游戏逆向分析实战

admin 2026-04-04 05:12:45 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文以cocos2d-xiOS游戏’好友赛’为案例,详细阐述了逆向分析的完整流程。通过开发cocos2dxfridatoolkit工具包,实现了对JavaScriptCore引擎的脚本拦截与解密,并针对性能优化和稳定性问题提供了具体解决方案。文章还介绍了将关键功能迁移至原生iOSTweak(CardRecorder)的开发思路,为移动游戏安全研究提供了实用的技术参考和实战经验。 综合评分: 85 文章分类: 逆向分析,移动安全,安全工具,实战经验


cover_image

Cocos2d-x iOS游戏逆向分析实战

Zedbully Zedbully

看雪学苑

2026年4月3日 18:11 上海

在移动游戏开发领域,Cocos2d-x 作为一款成熟的开源游戏引擎,被广泛应用于各类手游开发。然而,随着游戏安全需求的提升,对 Cocos2d-x 游戏的逆向分析也成为了安全研究人员和游戏开发者关注的重点。本文将深入剖析一个实际的 Cocos2d-x iOS 游戏逆向案例,分享完整的技术细节和实战经验。

#

一、目标分析:好友赛游戏

#

1.1 应用基本信息

  • 应用名称:好友赛 (haoyousai)
  • Bundle ID:com.oedere.lid23
  • 版本号:1.0 (Build 59)
  • 目标平台:iOS 15.0+
  • CPU架构:arm64
  • 游戏类型:棋牌类游戏

#

1.2 技术栈识别

通过静态分析和动态调试,我们识别出以下技术栈:

graph TD
A[Cocos2d-x游戏引擎] --> B[JavaScriptCore脚本引擎]
A --> C[OpenGL ES图形渲染]
B --> D[JSC字节码预编译]
B --> E[明文JS脚本]
    F[游戏逻辑] --> G[房间管理]
    F --> H[牌局处理]
    F --> I[用户交互]

1.3 应用结构分析

haoyousai.app/
├── haoyousai              # 主可执行文件 (15.9MB)
├── Frameworks/           # 依赖框架
├── script/              # 游戏脚本目录
├── src/                 # 源代码目录
├── res/                 # 资源文件
├── project.json         # Cocos项目配置
├── project.manifest     # 资源清单
└── main.js             # 入口JS文件

#

#

二、逆向工具开发

#

cocos2dx_frida_toolkit.js

2.1 工具架构设计

我们开发了一个全面的 Frida 逆向分析工具,整体架构如下:

// 工具架构示意图
class Cocos2dxFridaToolkit {
// 1. 基础模块
    - Helper Functions
    - Configuration Manager
    - Logger System

// 2. 检测模块
    - Cocos2dxDetector
    - ScriptEngineDetector

// 3. 分析模块
    - LuaScriptAnalyzer
    - JSScriptAnalyzer
    - CocosGameAnalyzer

// 4. 监控模块
    - InputOutputMonitor
    - PerformanceProfiler

// 5. 控制模块
    - ToolkitController
    - RPC Exports
}

2.2 关键技术实现

2.2.1 脚本引擎检测

class Cocos2dxDetector {
// 检测Lua引擎
detectLuaEngine() {
const exports = ['luaL_loadbuffer', 'lua_pcall', 'lua_getglobal'];
return this.findExportsInMainModule(exports);
    }

// 检测JavaScriptCore
detectJavaScriptCore() {
const exports = ['JSEvaluateScript', 'JSObjectCallAsFunction'];
return this.findExportsInMainModule(exports);
    }

// 检测SpiderMonkey
detectSpiderMonkey() {
const exports = ['JS_EvaluateScript', 'JS_ExecuteScript'];
return this.findExportsInMainModule(exports);
    }

// 检测Cocos JS绑定
detectCocosBindings() {
const patterns = ['jsb_', 'cocos2d::', 'ScriptingCore::'];
return this.searchExportsByPattern(patterns);
    }
}

2.2.2 脚本拦截与解密

Lua脚本拦截

class LuaScriptAnalyzer {
    hookLuaFunctions() {
// Hook luaL_loadbuffer 拦截Lua脚本加载
        Interceptor.attach(Module.findExportByName(null, 'luaL_loadbuffer'), {
            onEnter: function(args) {
const buffer = args[1];  // 脚本缓冲区
const size = args[2];    // 脚本大小
const chunkname = args[3]; // 脚本名称

// 提取并保存脚本
this.scriptData = Memory.readByteArray(buffer, size);
this.scriptName = Memory.readUtf8String(chunkname);
            },
            onLeave: function(retval) {
if (this.scriptData) {
this.saveLuaScript(this.scriptName, this.scriptData);
                }
            }
        });
    }
}

JavaScript脚本拦截

class JSScriptAnalyzer {
    hookJavaScriptCore() {
// Hook JSEvaluateScript 拦截JS执行
        Interceptor.attach(Module.findExportByName('JavaScriptCore', 'JSEvaluateScript'), {
            onEnter: function(args) {
const script = args[1];  // JS脚本字符串
const sourceURL = args[3]; // 源URL

// 读取脚本内容
const scriptStr = this.readJSString(script);
const urlStr = this.readJSString(sourceURL);

// 分析脚本内容
this.analyzeJSScript(scriptStr, urlStr);
            }
        });
    }

// 读取JS字符串的辅助函数
    readJSString(jsStringRef) {
const size = this.JSStringGetMaximumUTF8CStringSize(jsStringRef);
const buffer = Memory.alloc(size);
this.JSStringGetUTF8CString(jsStringRef, buffer, size);
return buffer.readUtf8String();
    }
}

2.3 性能优化与稳定性修复

在开发过程中,我们遇到了多个技术挑战并进行了优化:

2.3.1 超时崩溃问题修复

问题:原始实现中遍历所有模块导出和ObjC类,导致Frida超时。

解决方案

// 优化前:遍历所有模块
Process.enumerateModules().forEach(module => {
module.enumerateExports().forEach(export => {
// 处理每个导出
    });
});

// 优化后:只扫描主模块
const mainModule = Process.enumerateModules()[0];
mainModule.enumerateExports().forEach(export => {
// 只处理主模块导出
});

// 限制ObjC类遍历数量
const maxHooksPerCategory = CONFIG.maxHooksPerCategory || 50;
let hookCount = 0;
for (let className in ObjC.classes) {
if (hookCount >= maxHooksPerCategory) break;
// 处理ObjC类
    hookCount++;
}

2.3.2 Hook稳定性修复

问题:尝试Hook数据符号地址导致崩溃。

解决方案

function isExecutableAddress(address) {
const range = Process.findRangeByAddress(address);
return range && range.protection.includes('x');
}

function safeAttach(address, callbacks) {
if (!isExecutableAddress(address)) {
        logger.warn(`地址 ${address} 不可执行,跳过Hook`);
return null;
    }
return Interceptor.attach(address, callbacks);
}

2.3.3 ObjC桥接修复

问题:直接传递JS字符串给ObjC方法导致类型不匹配。

解决方案

function nsStr(jsString) {
// 使用NSString包装JS字符串
return ObjC.classes.NSString.stringWithUTF8String_(jsString);
}

function createDir(path) {
const fileManager = ObjC.classes.NSFileManager.defaultManager();
const nsPath = nsStr(path);
const errorPtr = Memory.alloc(Process.pointerSize);

// 正确传递参数
return fileManager.createDirectoryAtPath_withIntermediateDirectories_attributes_error_(
        nsPath,
1,  // YES
NULL,
        errorPtr
    );
}

#

三、原生Tweak开发:CardRecorder

#

3.1 设计思路

为了提供更稳定的游戏数据监控,我们开发了原生iOS Tweak,将关键功能从Frida脚本迁移到原生代码中。

3.2 核心实现

// CardRecorder.mm 核心代码分析

// 1. JavaScriptCore C-API Hook
__attribute__((constructor))
staticvoidCardRecorderInit(void) {
// 加载JavaScriptCore框架
void *jscHandle = dlopen("/System/Library/Frameworks/JavaScriptCore.framework/JavaScriptCore", RTLD_NOW);

// 获取JSEvaluateScript函数指针
    JSEvaluateScript_t orig_JSEvaluateScript =
        (JSEvaluateScript_t)dlsym(jscHandle, "JSEvaluateScript");

// 使用MSHookFunction进行Hook
MSHookFunction(
        (void *)orig_JSEvaluateScript,
        (void *)hook_JSEvaluateScript,
        (void **)&orig_JSEvaluateScript
    );
}

// 2. Hook函数实现
static JSValueRef hook_JSEvaluateScript(
    JSContextRef ctx,
    JSStringRef script,
    JSObjectRef thisObject,
    JSStringRef sourceURL,
int startingLineNumber,
    JSValueRef *exception){
// 捕获JSContext
if (ctx && !g_jsCtx) {
        g_jsCtx = ctx;
NSLog(@"[CardRecorder] 捕获JSContext: %p", ctx);
    }

// 检测游戏脚本
if (!g_injected && script) {
size_t maxSize = JSStringGetMaximumUTF8CStringSize(script);
if (maxSize > 5000) {  // 只处理大型脚本
char *buffer = (char *)malloc(maxSize);
JSStringGetUTF8CString(script, buffer, maxSize);

// 检测关键词"setRoomData"
if (strstr(buffer, "setRoomData") != NULL) {
NSLog(@"[CardRecorder] 检测到游戏脚本,准备注入监控代码");
injectCardMonitor(ctx);
            }
free(buffer);
        }
    }

// 调用原始函数
return orig_JSEvaluateScript(ctx, script, thisObject, sourceURL, startingLineNumber, exception);
}

// 3. 监控代码注入
staticvoidinjectCardMonitor(JSContextRef ctx) {
constchar *monitorJS =
"(function(){"
"  if(window.__cardHookInstalled) return;"
"  window.__cardHookInstalled = true;"
"  setInterval(function(){"
"    try {"
"      if(!iGame || !iGame.Data || !iGame.Data.roomData) return;"
"      var selfSeat = iGame.Data.getSelfSeatNo ? iGame.Data.getSelfSeatNo() : 0;"
"      var players = iGame.Data.roomData.players;"
"      var result = {self_seat: selfSeat, my_hold: [], players: []};"
"      players.forEach(function(p){"
"        if(p.seat_no === selfSeat){"
"          result.my_hold = (p.hold || []).filter(c => c > 0);"
"        }"
"        result.players.push({"
"          seat: p.seat_no,"
"          out: p.out || [],"
"          kou: p.kou || []"
"        });"
"      });"
"      window.__cardData = JSON.stringify(result);"
"    } catch(e){}"
"  }, 1000);"
"})();";

// 在游戏JSContext中执行监控代码
    JSStringRef jsStr = JSStringCreateWithUTF8CString(monitorJS);
    JSValueRef exception = NULL;
JSEvaluateScript(ctx, jsStr, NULL, NULL, 0, &exception);
JSStringRelease(jsStr);
}

3.3 数据采集与存储

// 定时数据采集
static void startCardPolling(void) {
    dispatch_source_t timer = dispatch_source_create(
        DISPATCH_SOURCE_TYPE_TIMER, 0, 0, g_queue);

    dispatch_source_set_timer(timer,
        dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC),
1 * NSEC_PER_SEC, 0);

    dispatch_source_set_event_handler(timer, ^{
if (!g_jsCtx || !g_injected) return;

// 读取游戏数据
NSString *cardData = evalInGameJS(g_jsCtx, "window.__cardData || """);

if (cardData && ![cardData isEqualToString:g_lastCardData]) {
            g_lastCardData = cardData;

// 保存到文件
            [self appendCardLog:cardData];
        }
    });

    dispatch_resume(timer);
}

// 文件存储
- (void)appendCardLog:(NSString *)json {
NSString *docPath = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *logPath = [docPath stringByAppendingPathComponent:@"card_log.json"];

NSFileManager *fm = [NSFileManager defaultManager];
if (![fm fileExistsAtPath:logPath]) {
        [fm createFileAtPath:logPath contents:nil attributes:nil];
    }

NSFileHandle *fh = [NSFileHandle fileHandleForWritingAtPath:logPath];
    [fh seekToEndOfFile];
    [fh writeData:[[json stringByAppendingString:@"\n"] dataUsingEncoding:NSUTF8StringEncoding]];
    [fh closeFile];
}

#

四、动态分析结果

#

4.1 脚本捕获与分析

通过我们的工具,成功捕获了以下关键脚本:

4.1.1 JSC字节码文件

scripts/
├── G212.jsc_1774770976633.bin    # 游戏模块212 (101KB)
├── G30.jsc_1774770909867.bin     # 游戏模块30 (29KB)
├── SYG30.jsc_1774770968778.bin   # 系统模块30 (49KB)
└── SYHall.jsc_1774770963515.bin  # 大厅模块 (584KB)

4.1.2 明文JS脚本

scripts/
├── js_1774770963709.js          # 主游戏逻辑 (1.7MB)
├── js_1774770976843.js          # 游戏模块 (302KB)
├── js_1774770968967.js          # 系统模块 (147KB)
└── 多个小型配置脚本 (88B-2KB)

4.2 游戏架构解析

通过分析捕获的脚本,我们还原了游戏的架构:

// 游戏全局对象结构
window.iGame = {
Data: {
roomData: {
players: [{
seat_no: number,      // 座位号
hold: number[],       // 手牌
out: number[],        // 出牌
kou: number[]         // 扣牌
            }],
room_id: string,          // 房间ID
game_type: number         // 游戏类型
        },
getSelfSeatNo: function() {   // 获取自己座位号
return number;
        },
setRoomData: function(data) { // 设置房间数据
// 游戏状态更新
        }
    },

UI: {
// UI相关方法
    },

Network: {
// 网络通信方法
    }
};

4.3 游戏状态监控

我们的工具能够实时监控游戏状态:

{
"timestamp":"2026-03-29T21:00:00Z",
"self_seat":1,
"my_hold":[11,12,13,14,15],
"players":[
{
"seat":1,
"out":[21,22],
"kou":[]
},
{
"seat":2,
"out":[31],
"kou":[41,42]
},
{
"seat":3,
"out":[],
"kou":[51]
},
{
"seat":4,
"out":[61,62,63],
"kou":[]
}
]
}

#

五、技术难点与解决方案

5.1 多脚本引擎支持

难点:Cocos2d-x支持多种脚本引擎(Lua、JavaScriptCore、SpiderMonkey)。

解决方案

class ScriptEngineDetector {
    detectAllEngines() {
const engines = [];

// 检测Lua
if (this.detectLuaEngine()) {
            engines.push({ type: 'lua', version: this.getLuaVersion() });
        }

// 检测JavaScriptCore
if (this.detectJavaScriptCore()) {
            engines.push({ type: 'javascriptcore', version: this.getJSCVersion() });
        }

// 检测SpiderMonkey
if (this.detectSpiderMonkey()) {
            engines.push({ type: 'spidermonkey', version: this.getSMVersion() });
        }

return engines;
    }
}

5.2 脚本加密与混淆

难点:游戏脚本可能被加密或混淆。

解决方案

class ScriptDecryptor {
    decryptScript(encryptedData, encryptionType) {
        switch (encryptionType) {
            case 'xor':
return this.xorDecrypt(encryptedData, this.findXorKey());
            case 'base64':
return this.base64Decode(encryptedData);
            case 'custom':
return this.customDecrypt(encryptedData);
            default:
return encryptedData; // 可能未加密
        }
    }

    xorDecrypt(data, key) {
const decrypted = [];
for&nbsp;(let i =&nbsp;0; i <&nbsp;data.length; i++) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; decrypted.push(data[i] ^ key[i % key.length]);
&nbsp; &nbsp; &nbsp; &nbsp; }
return&nbsp;Buffer.from(decrypted);
&nbsp; &nbsp; }

&nbsp; &nbsp; findXorKey() {
// 通过模式识别或动态分析查找XOR密钥
const&nbsp;commonPatterns = [
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [0x73,&nbsp;0x63,&nbsp;0x72,&nbsp;0x69,&nbsp;0x70,&nbsp;0x74],&nbsp;// "script"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [0x67,&nbsp;0x61,&nbsp;0x6D,&nbsp;0x65], &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// "game"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [0x63,&nbsp;0x6F,&nbsp;0x63,&nbsp;0x6F,&nbsp;0x73] &nbsp; &nbsp; &nbsp; &nbsp;// "cocos"
&nbsp; &nbsp; &nbsp; &nbsp; ];

// 尝试常见密钥
for&nbsp;(const&nbsp;pattern of commonPatterns) {
if&nbsp;(this.testXorKey(pattern)) {
return&nbsp;pattern;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; }

// 动态分析查找
return&nbsp;this.dynamicFindXorKey();
&nbsp; &nbsp; }
}

5.3 性能与稳定性平衡

难点:Hook过多影响游戏性能,Hook过少无法获取足够信息。

解决方案

class&nbsp;PerformanceOptimizer&nbsp;{
constructor() {
this.hookStats = {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; totalHooks:&nbsp;0,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; activeHooks:&nbsp;0,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; performanceImpact:&nbsp;0
&nbsp; &nbsp; &nbsp; &nbsp; };

this.config = {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; maxHooks:&nbsp;200,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; samplingRate:&nbsp;0.1,&nbsp;// 10%采样率
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; enableLazyHook:&nbsp;true
&nbsp; &nbsp; &nbsp; &nbsp; };
&nbsp; &nbsp; }

&nbsp; &nbsp; shouldHookFunction(funcName, importance) {
// 根据重要性决定是否Hook
if&nbsp;(importance >=&nbsp;0.8)&nbsp;return&nbsp;true;&nbsp;// 高重要性函数

if&nbsp;(this.hookStats.totalHooks >=&nbsp;this.config.maxHooks) {
return&nbsp;false;&nbsp;// 达到Hook上限
&nbsp; &nbsp; &nbsp; &nbsp; }

// 使用采样率控制Hook数量
if&nbsp;(Math.random() <&nbsp;this.config.samplingRate) {
return&nbsp;true;
&nbsp; &nbsp; &nbsp; &nbsp; }

return&nbsp;false;
&nbsp; &nbsp; }

&nbsp; &nbsp; lazyHook(address, callbacks, options = {}) {
if&nbsp;(this.config.enableLazyHook && options.lazy) {
// 延迟Hook,只在需要时激活
return&nbsp;new LazyHook(address, callbacks);
&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp;else&nbsp;{
return&nbsp;Interceptor.attach(address, callbacks);
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }
}

class&nbsp;LazyHook&nbsp;{
constructor(address, callbacks) {
this.address = address;
this.callbacks = callbacks;
this.active =&nbsp;false;
this.interceptor =&nbsp;null;
&nbsp; &nbsp; }

&nbsp; &nbsp; activate() {
if&nbsp;(!this.active) {
this.interceptor = Interceptor.attach(this.address,&nbsp;this.callbacks);
this.active =&nbsp;true;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }

&nbsp; &nbsp; deactivate() {
if&nbsp;(this.active &&&nbsp;this.interceptor) {
this.interceptor.detach();
this.active =&nbsp;false;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }
}

#

六、实战应用场景

#

6.1 游戏逻辑分析

通过我们的工具,可以深入分析游戏的核心逻辑:

// 分析游戏状态机
class&nbsp;GameStateAnalyzer&nbsp;{
analyzeStateMachine() {
const&nbsp;states =&nbsp;new&nbsp;Set();
const&nbsp;transitions = [];

// Hook状态切换函数
Interceptor.attach(this.findFunction('changeGameState'), {
onEnter:&nbsp;function(args) {
const&nbsp;oldState = args[0];
const&nbsp;newState = args[1];

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; states.add(oldState.toString());
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; states.add(newState.toString());
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; transitions.push({
from: oldState.toString(),
to: newState.toString(),
timestamp:&nbsp;Date.now()
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; });
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; });

return&nbsp;{
states:&nbsp;Array.from(states),
transitions: transitions,
graph:&nbsp;this.generateStateGraph(transitions)
&nbsp; &nbsp; &nbsp; &nbsp; };
&nbsp; &nbsp; }
}

6.2 网络协议分析

class&nbsp;NetworkProtocolAnalyzer&nbsp;{
&nbsp; &nbsp; analyzeNetworkProtocol() {
// Hook网络发送函数
&nbsp; &nbsp; &nbsp; &nbsp; Interceptor.attach(Module.findExportByName(null,&nbsp;'send'), {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; onEnter: function(args) {
const&nbsp;socket = args[0];
const&nbsp;buffer = args[1];
const&nbsp;length = args[2];

const&nbsp;data&nbsp;= Memory.readByteArray(buffer, length);
this.packet = {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; type:&nbsp;'send',
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; socket: socket,
data:&nbsp;data,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; length: length,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; timestamp: Date.now()
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; };
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; onLeave: function(retval) {
this.analyzePacket(this.packet);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; });

// Hook网络接收函数
&nbsp; &nbsp; &nbsp; &nbsp; Interceptor.attach(Module.findExportByName(null,&nbsp;'recv'), {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; onEnter: function(args) {
const&nbsp;socket = args[0];
const&nbsp;buffer = args[1];
const&nbsp;length = args[2];

this.socket = socket;
this.buffer = buffer;
this.length = length;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; onLeave: function(retval) {
if&nbsp;(retval >&nbsp;0) {
const&nbsp;data&nbsp;= Memory.readByteArray(this.buffer, retval);
const&nbsp;packet = {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; type:&nbsp;'recv',
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; socket:&nbsp;this.socket,
data:&nbsp;data,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; length: retval,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; timestamp: Date.now()
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; };
this.analyzePacket(packet);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; });
&nbsp; &nbsp; }

&nbsp; &nbsp; analyzePacket(packet) {
// 协议解析逻辑
const&nbsp;header = packet.data.slice(0,&nbsp;4);
const&nbsp;body = packet.data.slice(4);

&nbsp; &nbsp; &nbsp; &nbsp; console.log(`[Network] ${packet.type} packet:`, {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; length: packet.length,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; header: header.toString('hex'),
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bodyLength: body.length
&nbsp; &nbsp; &nbsp; &nbsp; });
&nbsp; &nbsp; }
}

6.3 自动化测试框架

class&nbsp;AutomatedTestFramework&nbsp;{
constructor() {
this.testCases&nbsp;= [];
this.results&nbsp;= [];
&nbsp; &nbsp; }

addTestCase(name, setup, execute, verify) {
this.testCases.push({
name: name,
setup: setup,
execute: execute,
verify: verify,
status:&nbsp;'pending'
&nbsp; &nbsp; &nbsp; &nbsp; });
&nbsp; &nbsp; }

runTests() {
console.log(`开始执行&nbsp;${this.testCases.length}&nbsp;个测试用例`);

this.testCases.forEach((testCase, index) =>&nbsp;{
console.log(`[${index +&nbsp;1}/${this.testCases.length}] 执行测试:&nbsp;${testCase.name}`);

try&nbsp;{
// 执行测试
const&nbsp;context = testCase.setup();
const&nbsp;result = testCase.execute(context);
const&nbsp;passed = testCase.verify(result);

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; testCase.status&nbsp;= passed ?&nbsp;'passed'&nbsp;:&nbsp;'failed';
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; testCase.result&nbsp;= result;

console.log(` &nbsp;✓ 测试&nbsp;${testCase.name}:&nbsp;${passed ? '通过' : '失败'}`);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp;catch&nbsp;(error) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; testCase.status&nbsp;=&nbsp;'error';
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; testCase.error&nbsp;= error.message;
console.log(` &nbsp;✗ 测试&nbsp;${testCase.name}: 错误 -&nbsp;${error.message}`);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; });

return&nbsp;this.generateReport();
&nbsp; &nbsp; }

generateReport() {
const&nbsp;passed =&nbsp;this.testCases.filter(tc&nbsp;=>&nbsp;tc.status&nbsp;===&nbsp;'passed').length;
const&nbsp;failed =&nbsp;this.testCases.filter(tc&nbsp;=>&nbsp;tc.status&nbsp;===&nbsp;'failed').length;
const&nbsp;errors =&nbsp;this.testCases.filter(tc&nbsp;=>&nbsp;tc.status&nbsp;===&nbsp;'error').length;

return&nbsp;{
summary: {
total:&nbsp;this.testCases.length,
passed: passed,
failed: failed,
errors: errors,
successRate: (passed /&nbsp;this.testCases.length&nbsp;*&nbsp;100).toFixed(2) +&nbsp;'%'
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },
details:&nbsp;this.testCases.map(tc&nbsp;=>&nbsp;({
name: tc.name,
status: tc.status,
result: tc.result,
error: tc.error
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }))
&nbsp; &nbsp; &nbsp; &nbsp; };
&nbsp; &nbsp; }
}

#

七、安全防护建议

7.1 针对逆向分析的防护措施

基于我们的逆向经验,为游戏开发者提供以下防护建议:

// 1. 代码混淆
class&nbsp;CodeObfuscator&nbsp;{
&nbsp; &nbsp; obfuscateJavaScript(code) {
// 变量名混淆
&nbsp; &nbsp; &nbsp; &nbsp; code =&nbsp;this.renameVariables(code);

// 控制流扁平化
&nbsp; &nbsp; &nbsp; &nbsp; code =&nbsp;this.flattenControlFlow(code);

// 字符串加密
&nbsp; &nbsp; &nbsp; &nbsp; code =&nbsp;this.encryptStrings(code);

// 死代码插入
&nbsp; &nbsp; &nbsp; &nbsp; code =&nbsp;this.insertDeadCode(code);

return&nbsp;code;
&nbsp; &nbsp; }
}

// 2. 反调试检测
class&nbsp;AntiDebugDetector&nbsp;{
&nbsp; &nbsp; checkDebuggers() {
const&nbsp;checks = [
this.checkFrida(),
this.checkPtrace(),
this.checkSysctl(),
this.checkExceptionPorts()
&nbsp; &nbsp; &nbsp; &nbsp; ];

return&nbsp;checks.some(check => check ===&nbsp;true);
&nbsp; &nbsp; }

&nbsp; &nbsp; checkFrida() {
// 检测Frida特征
const&nbsp;fridaSignatures = [
'frida-agent',
'gum-js-loop',
'libfrida'
&nbsp; &nbsp; &nbsp; &nbsp; ];

const&nbsp;modules = Process.enumerateModules();
return&nbsp;modules.some(module => {
return&nbsp;fridaSignatures.some(sig =>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; module.name.includes(sig) || module.path.includes(sig)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; );
&nbsp; &nbsp; &nbsp; &nbsp; });
&nbsp; &nbsp; }
}

// 3. 运行时完整性校验
class&nbsp;IntegrityChecker&nbsp;{
&nbsp; &nbsp; verifyIntegrity() {
// 校验代码段完整性
const&nbsp;textSegment = Process.getModuleByName('haoyousai');
const&nbsp;expectedHash =&nbsp;this.calculateHash(textSegment.base, textSegment.size);
const&nbsp;actualHash =&nbsp;this.readStoredHash();

if&nbsp;(expectedHash !== actualHash) {
this.handleTamperingDetected();
&nbsp; &nbsp; &nbsp; &nbsp; }

// 校验关键函数
this.verifyCriticalFunctions();
&nbsp; &nbsp; }
}

7.2 数据加密建议

// 使用强加密保护敏感数据
class&nbsp;DataProtector&nbsp;{
&nbsp; &nbsp; encryptGameData(data, key) {
// 使用AES-GCM加密
const&nbsp;iv = crypto.randomBytes(12);
const&nbsp;cipher = crypto.createCipheriv('aes-256-gcm', key, iv);

const&nbsp;encrypted = Buffer.concat([
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cipher.update(data),
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cipher.final()
&nbsp; &nbsp; &nbsp; &nbsp; ]);

const&nbsp;authTag = cipher.getAuthTag();

return&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iv: iv.toString('hex'),
data: encrypted.toString('hex'),
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tag: authTag.toString('hex')
&nbsp; &nbsp; &nbsp; &nbsp; };
&nbsp; &nbsp; }

&nbsp; &nbsp; decryptGameData(encrypted, key) {
const&nbsp;iv = Buffer.from(encrypted.iv,&nbsp;'hex');
const&nbsp;data&nbsp;= Buffer.from(encrypted.data,&nbsp;'hex');
const&nbsp;tag = Buffer.from(encrypted.tag,&nbsp;'hex');

const&nbsp;decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
&nbsp; &nbsp; &nbsp; &nbsp; decipher.setAuthTag(tag);

return&nbsp;Buffer.concat([
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; decipher.update(data),
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; decipher.final()
&nbsp; &nbsp; &nbsp; &nbsp; ]);
&nbsp; &nbsp; }
}

#

八、总结与展望

8.1 技术总结

通过本次逆向分析实战,我们取得了以下成果:

  • 完整工具链开发:开发了从动态分析到原生Tweak的完整工具链
  • 深度游戏理解:深入理解了Cocos2d-x游戏的工作原理和架构
  • 稳定性优化:解决了多个关键技术难题,提升了工具稳定性
  • 实用价值:工具具有实际应用价值,可用于游戏分析、安全测试等场景

8.2 技术亮点

  • 多引擎支持:同时支持Lua、JavaScriptCore、SpiderMonkey等多种脚本引擎
  • 性能优化:通过智能Hook管理和采样技术平衡性能与信息获取
  • 稳定性保障:完善的错误处理和恢复机制
  • 扩展性设计:模块化设计便于功能扩展和维护

8.3 未来展望

  • AI辅助分析:结合机器学习技术自动识别游戏模式和逻辑
  • 跨平台支持:扩展到Android平台和更多游戏引擎
  • 云端分析:提供云端游戏分析服务
  • 自动化报告:自动生成详细的分析报告和安全评估

8.4 开源计划

我们计划将核心工具开源,包括:

  • cocos2dx_frida_toolkit.js:完整的Frida逆向分析工具
  • CardRecorder:原生iOS Tweak实现
  • 示例脚本和文档
  • 常见游戏的分析模板

#

九、致辞

感谢以下开源项目和工具的支持:

  • Frida:动态插桩框架
  • Theos:iOS越狱开发工具链
  • Cocos2d-x:开源游戏引擎
  • JavaScriptCore:Apple JavaScript引擎

十、参考资料

#

十、参考资料

  • Frida官方文档:https://frida.re/docs/
  • Cocos2d-x官方文档:https://docs.cocos2d-x.org/
  • iOS逆向工程指南
  • JavaScriptCore内部原理

版权声明:本文仅供技术学习和研究使用,请勿用于非法用途。任何商业使用需获得原作者授权。联系方式:通过技术论坛私信联系

更新日志

  • v1.0 (2026-03-29):初始版本发布
  • v1.1 (计划):增加Android平台支持

#

#

看雪ID:Zedbully

https://bbs.kanxue.com/user-home-995236.htm

*本文为看雪论坛优秀文章,由 Zedbully 原创,转载请注明来自看雪社区

往期推荐

一次尝试某APP签名算法逆向追踪:从抓包到SO层

Pixel 8a(akita:6.1-android16内核支持ebpf)AOSP/GKI内核源码获取、编译与刷机实战指南

SUCTF2026 Ez_Router

APP frida 检测绕过详解:定位 JNI 动态注册 Native 函数,Hook 核心检测函数

手游逆向全流程复盘:从 IL2CPP Dump 到 TCP 握手协议还原

球分享

球点赞

球在看

点击阅读原文查看更多


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:看雪学苑 Zedbully Zedbully《Cocos2d-x iOS游戏逆向分析实战》

2025年第3卷第5期 网络安全文章

2025年第3卷第5期

文章总结: 本文为网络空间安全科学学报2025年第3卷第5期目录与摘要合集,专题聚焦网络信息系统安全性评测,涵盖ATT&CK技术关联分析、深度伪造语音检测、边缘
评论:0   参与:  0