首页
社区
课程
招聘
[原创]iOS手游逆向之旅-cocos2dx引擎的luac解密提取及修改
发表于: 2022-8-18 01:33 35006

[原创]iOS手游逆向之旅-cocos2dx引擎的luac解密提取及修改

2022-8-18 01:33
35006

在上篇中我们确定了《问*》手游所使用的游戏引擎是cocos2dx-3.2版本
通过查看目录文件 在res目录中出现大量的文件

但通过查看发现并非是lua文件 部分是JSON数据 部分不可读 查看不可读文件的hex 如下图

看起来是某种文件格式 搜索无果 怀疑是加密后的
其实也正常 游戏安全对抗这么多年 游戏厂商或多或少都会防着点 并不会让我们轻易就做到想要的
下面我们直接从游戏引擎入手来找出游戏的lua代码

 

cocos官方文档:https://www.cocos.com/docs#2dx
cocos2dx引擎源码:https://github.com/cocos2d/cocos2d-x

 

查看官方文档关于脚本的使用
https://docs.cocos.com/cocos2d-x/manual/zh/scripting/

可以看到有提供demo 我们先下载demo跟着官方教程来学习下cocos2dx是如何加载lua的

 

搭建开发环境 - iOS
https://docs.cocos.com/cocos2d-x/manual/zh/installation/iOS.html

 

跟着教程下载demo后查看源码

可以看到demo中加载lua的流程是如下

1
2
LuaEngine* pEngine = LuaEngine::getInstance();
pEngine->executeScriptFile("controller.lua");

代码很简单 通过调用executeScriptFile来加载文件 我们下载cocos2dx版本源码来查看函数的实现
源码中搜索查看到LuaEngine::executeScriptFile的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
int LuaStack::executeScriptFile(const char* filename)
{
    CCAssert(filename, "CCLuaStack::executeScriptFile() - invalid filename");
 
    std::string buf(filename);
    //
    // remove .lua or .luac
    //
    size_t pos = buf.rfind(BYTECODE_FILE_EXT);
    if (pos != std::string::npos)
    {
        buf = buf.substr(0, pos);
    }
    else
    {
        pos = buf.rfind(NOT_BYTECODE_FILE_EXT);
        if (pos == buf.length() - NOT_BYTECODE_FILE_EXT.length())
        {
            buf = buf.substr(0, pos);
        }
    }
 
    FileUtils *utils = FileUtils::getInstance();
 
    //
    // 1. check .luac suffix
    // 2. check .lua suffix
    //
    std::string tmpfilename = buf + BYTECODE_FILE_EXT;
    if (utils->isFileExist(tmpfilename))
    {
        buf = tmpfilename;
    }
    else
    {
        tmpfilename = buf + NOT_BYTECODE_FILE_EXT;
        if (utils->isFileExist(tmpfilename))
        {
            buf = tmpfilename;
        }
    }
 
    std::string fullPath = utils->fullPathForFilename(buf);
    Data data = utils->getDataFromFile(fullPath);
    int rn = 0;
    if (!data.isNull())
    {
        if (luaLoadBuffer(_state, (const char*)data.getBytes(), (int)data.getSize(), fullPath.c_str()) == 0)
        {
            rn = executeFunction(0);
        }
    }
    return rn;
}

可以看到utils->getDataFromFile读取到文件后 通过luaLoadBuffer函数加载 我们继续搜索luaLoadBuffer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
int LuaStack::luaLoadBuffer(lua_State *L, const char *chunk, int chunkSize, const char *chunkName)
{
    int r = 0;
 
    if (_xxteaEnabled && strncmp(chunk, _xxteaSign, _xxteaSignLen) == 0)
    {
        // decrypt XXTEA
        xxtea_long len = 0;
        unsigned char* result = xxtea_decrypt((unsigned char*)chunk + _xxteaSignLen,
                                              (xxtea_long)chunkSize - _xxteaSignLen,
                                              (unsigned char*)_xxteaKey,
                                              (xxtea_long)_xxteaKeyLen,
                                              &len);
        unsigned char* content = result;
        xxtea_long contentSize = len;
        skipBOM((const char*&)content, (int&)contentSize);
        r = luaL_loadbuffer(L, (char*)content, contentSize, chunkName);
        free(result);
    }
    else
    {
        skipBOM(chunk, chunkSize);
        r = luaL_loadbuffer(L, chunk, chunkSize, chunkName);
    }
 
#if defined(COCOS2D_DEBUG) && COCOS2D_DEBUG > 0
    if (r)
    {
        switch (r)
        {
            case LUA_ERRSYNTAX:
                CCLOG("[LUA ERROR] load \"%s\", error: syntax error during pre-compilation.", chunkName);
                break;
 
            case LUA_ERRMEM:
                CCLOG("[LUA ERROR] load \"%s\", error: memory allocation error.", chunkName);
                break;
 
            case LUA_ERRFILE:
                CCLOG("[LUA ERROR] load \"%s\", error: cannot open/read file.", chunkName);
                break;
 
            default:
                CCLOG("[LUA ERROR] load \"%s\", error: unknown.", chunkName);
        }
    }
#endif
    return r;
}

根据代码看到如果有加密则调用xxtea_decrypt进行解密 解密后调用luaL_loadbuffer加载脚本

 

再继续搜索luaL_loadbuffer并没有实现 只有头部定义了 到这里应该就是lua解密后的加载了

 

虽然没有开发过cocos2dx手游 但根据源码来看 现在就已经清楚cocos2dx是如何加载加密和未加密lua的了

 

那么接下来提取解密后的lua文件有两件事要做
1.找到手游中luaL_loadbuffer函数的地址
2.hook luaL_loadbuffer函数提取解密后的lua代码

 

那么接下来分析luaL_loadbuffer函数地址

 

在cocos2dx加载lua的代码中 我们看到代码中存在CCLOG 目测是日志打印 后面紧跟着字符串 我们来在IDA中尝试搜索下"[LUA ERROR] load \"%s\", error: unknown."

就很棒 一下找到 我们双击字符串进去

按下快捷键X查找交叉引用

再次双击

来到了使用该字符串的函数位置 上图为ARM64汇编
看不懂没关系 尝试F5看看能不能还原为伪代码

找出luaLoadBuffer代码对比下

对比看下来就很眼熟 不能说一样 简直一摸一样
不管是不是 直接上HOOK 先打印一下看看数据再说
编写Tweak来Hook函数地址

1
2
3
4
5
6
7
8
9
10
int (*orig_LuaStack_luaL_LoadBuffer)(void *L, const char *buff, size_t size, const char *name);
int hook_LuaStack_luaL_LoadBuffer(void *L, const char *buff, size_t size, const char *name) {
    NSLog(@"[hook] hook_LuaStack_luaL_LoadBuffer name: %s size:%d buff:%s ", name, size, buff);
    return orig_LuaStack_luaL_LoadBuffer(L, buff, size, name);
}
 
void __attribute__((constructor)) hook() {
    uint64_t base = _dyld_get_image_vmaddr_slide(0);
    MSHookFunction((void *)(0x100000000+base+0x11DD728), (void *)hook_LuaStack_luaL_LoadBuffer, (void **)&orig_LuaStack_luaL_LoadBuffer);
}

hook后启动游戏 进入游戏

 

可以看到 已经hook到lua的加载 不过buff看起来不是文本的 我们将hook到的保存下来查看
查看hex

看到已经出现部分明文了 但是并不是文本代码
hex头部有字符串LuaQ 直接搜索引擎搜索下"LuaQ"

查阅资料得知luaQ是lua5.1 也就是luac 可以直接使用工具将luac文件转为lua文件
从网上下载unluac.jar文件
使用命令执行

1
java -jar unluac.jar ./StoreItemDlg.luac > ./StoreItemDlg.luac.lua

查看lua文件 发现成功转为lua文件了 代码已经可以阅读

 

下面来修改lua代码实现我们想要的功能并让游戏加载我们修改后的lua

 

未完待续


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2022-8-18 01:40 被尐进编辑 ,原因:
收藏
免费 2
支持
分享
最新回复 (6)
雪    币: 551
活跃值: (1522)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
思路很清晰!
2022-8-18 10:14
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
这个hook相关代码有完整吗
2022-9-26 15:42
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
有个破解需求,可以给个联系方式吗。
2022-10-27 09:26
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
联系方式:2198417406
2022-11-2 22:30
0
雪    币: 0
活跃值: (10)
能力值: (RANK:0 )
在线值:
发帖
回帖
粉丝
6
2023-4-28 02:23
0
雪    币: 0
活跃值: (10)
能力值: (RANK:0 )
在线值:
发帖
回帖
粉丝
7
楼主联系多少
2023-4-28 02:24
0
游客
登录 | 注册 方可回帖
返回
//