首页
社区
课程
招聘
[原创] 一个 Cocos for Lua 伪网游的破解实战
发表于: 2018-6-15 18:04 18762

[原创] 一个 Cocos for Lua 伪网游的破解实战

2018-6-15 18:04
18762

最近闲来无事,花了5个小时的时间学习破解了1款基于cocos lua写的android游戏,最终得到了这个游戏的源代码。第一次尽然轻松成功了,经验教训分享一下。

阅读当前教程,你需要掌握一下的一些技术和方法。

首先拿到APK后,使用ZIP打开查看APP内部所包含的文件。大部分游戏都是使用开源游戏引擎开发完成,所以发现所使用的引擎就是第一目标了。   


解压之后首先对classes.dex进行反编译操作(简你单起见,使用APKDB操作),得到java层的代码。将dex转换成jar之后,使用jd-gui查看代码:


从上图可知,当前的游戏使用了cocos2d-x 游戏引擎。因此我们的目标锁定到了native-code上。同时对APK目录结构的分析,发现当前游戏使用了脚本语言完成。


使用Hex Editor拖入任意一个nts,发现当前内容为乱码,可以认定当前脚本已经编译过或者加密过。


既然脚本编译过或者加密过,因此破解的任务首先是确定脚本的类型,然后还原脚本所加密的内容。因此打开IDA PRO,将当前的程序的so拖入到IDA中。


从左边的窗口中可以清楚的看到当前native代码的函数名(这个开发比较那啥,没对so做任何安全措施)。

反编译轻轻松松了,通过对函数名称的排序和阅读:


发现当前游戏所使用的脚本为lua。因此下一步操作目的就是定位lua解密和lua加载的方法。

在cocos游戏的启动过程,以下几个函数尤为重要,本文不涉及cocos的启动流程,具体流程百度介绍。当cocos初始化完成之后会调用applicationDidFinishLaunching()。因此定位到当前的函数查看程序行为。



进入当前函数,对当前函数进行反编译,可以看到大致的程序行为。

其中的

sub_CD11F958(&v11, (int)"Script/Main.nts", (int)&v10, *(_DWORD *)v3, (int)v10, v11, v12, v13);

这个函数为当前游戏的入口,所有的逻辑通过该入口进入脚本层。

同时我们可以看到还有一个很关键的函数叫做

AppDelegate::InitLuaStatus(v3);

从字面意思可以看出这个函数初始了lua的上下文环境。跟踪进去继续查看。


发现当前App通过这个函数

cocos2d::extension::ExportToLuaInit(*((cocos2d::extension **)v1 + 11), v5);


将所有运行时的一些方法导入到了脚本环境中。

当前函数做了四个操作,

cocos上下文环境初始化

BOX2D环境初始化

自定义的一些初始化

文件回调方法注册。(这个方法最为关键)

LuaPlus::SetFileGetWay(

           (int (__fastcall *)(_DWORD, _DWORD, _DWORD))MallocFile,

           (int (__fastcall *)(_DWORD))FreeFile);


当前方法像成员变量注册了两个回调方法,一个为分配内存是调用,一个为删除脚本是调用;

由于lua执行之前需要调用luaL_loadbuffer 或者luaL_loadbufferx 方法,前者读取的buffer未编译过的lua脚本,后者调用luac编译后的脚本。因此在代码中定位这两个方法,找到脚本执行之前的操作。


搜索符号表发现存在luaL_loadbufferx方法,在当前方法上按X进入调用栈


发现以下方法调用了luaL_loadbufferx,进入LuaPlus::ExecuteBufferRet函数中,


在当前的函数中,调用了luaL_loadbufferx,通过官方文档luaL_loadbufferx的函数原型为。其中第二个参数为要执行的Buffer。

luaL_loadbufferx (lua_State *L,

                               const char *buff,

                               size_t sz,

                               const char *name,

                               const char *mode);

同时在当前函数中,尚未发现任何于加解密有关的操作,因此判断加密操作在当前函数之前调用,按X查看LuaPlus::ExecuteBufferRet 函数的调用关系。我们来到了


LuaPlus::ExecuteFile(int a1, int a2, int *a3, int a4)

通过对这段代码的阅读,发现LuaPlus::g_pMallocFileData(a3, &buffer, &size);这个函数参加了文件读取与解密的操作。

上文提到,LuaPlus::g_pMallocFileData实际上是一个函数指针,因此回到LuaPlus::SetFileGetWay这个函数我们定位到了真正读文件和加密的函数。


进入当前函数,


大致阅读,发现红框中的函数为解密函数,其中byte_CD184CB 为一个常量数组,点击进入到常量段可以发现


当前的数组值为:

uint8_t keyBox[] =

{

0xA1, 0xA4, 0xA7, 0xAA, 0xAD, 0xB1, 0xB4, 0xB7, 0xBA,

 0xBD, 0xC1, 0xC4, 0xC7, 0xCA, 0xCD, 0xD1, 0xD4

};

根据上述伪代码写出解密代码(java 实现):


即可得出真正的明文数据。

对其中的一个文件进行解密操作:


很明显的看出已经得到了近似的代码。

继续分析:

通过上述截图,我们可以看出前面的四个字节以此为:

0x1B 0x4C 0x75 0x61 0x53

翻阅lua文档我们看出,前面四个字节为lua 的magic number,第五个字节为当lua的版

本,即lua5.3

typedef struct {

       char signature[4]; //".lua"

       uchar version;

       uchar format;

       uchar endian;

       uchar size_int;

       uchar size_size_t;

       uchar size_Instruction;

       uchar size_lua_Number;

       uchar lua_num_valid;

       uchar luac_tail[0x6];

} GlobalHeader;

因此使用unluac 工具我们可以得到源码。


最后发现,大功告成!

上一张提到了逆向+解密+反编译获取源码,这一张讲一下获得源码之后如何修改。

反编译源码之后,对立面一些源码进行查看,发现App使用以下的行为对App自身进行保护。

签名校验

当加载进从 Script/Main.nts 进入lua 脚本之后


Main脚本主要加载公共的脚本进行初始化之后,加载游戏世界。

进入Include.nts进行查看。


这个里面初始化了很多公共的脚本,其中一个关键的脚本为Security。这个脚本做了App自保操作。


在上图红色的地方进行了签名校验,如果签名不对游戏退出。

同时在代码里面搜索AppSignature()发现在其他的地方加载了当前的函数。


由于World.lua 可能由于热更新加载到外部的脚本,因此这个地方不做剔除操作。

从上面的大致逻辑可以看出游戏的整个执行过程。


上图为简化的游戏启动流程,其中红色的节点是有可能动态的从外部加载的。


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2018-6-15 18:05 被法老王ms编辑 ,原因:
收藏
免费 5
支持
分享
最新回复 (12)
雪    币: 2575
活跃值: (502)
能力值: ( LV2,RANK:85 )
在线值:
发帖
回帖
粉丝
2
分析的详细!
2018-6-15 18:07
0
雪    币: 15
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
收下我的膝盖
2018-6-16 08:32
0
雪    币: 97
活跃值: (86)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
支持一下大佬。。。
2018-6-16 19:11
0
雪    币: 4
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ll
5
非常牛逼
2018-8-26 19:35
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
如何联系你
2019-11-10 15:45
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
直接修改lua,自己改本地数据。
如果数据都保存在服务端,有什么思路做吗,如闪烁之光
2019-12-14 11:22
0
雪    币: 8
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
收下我的膝盖
2020-2-17 19:30
0
雪    币: 22
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
大佬留下联系方式  我找你弄东西
2020-3-30 21:48
0
雪    币: 298
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
10
大佬厉害了。
2020-5-13 13:16
0
雪    币: 272
活跃值: (97)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
11
mb_dsumamys 直接修改lua,自己改本地数据。 如果数据都保存在服务端,有什么思路做吗,如闪烁之光
可以拦截 收到的服务端的消息 然后进行操作。但是这个得涉及到 数据包的加解密了
2020-7-2 19:23
0
雪    币: 272
活跃值: (97)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
12
一般本地修改游戏 大概都是无敌之类的东西,你伪造过去的数据包服务端也要进行校验,一般成功率很低
2020-7-2 19:24
0
游客
登录 | 注册 方可回帖
返回
//