手游安全之cocos2d-x的源码浅析(手游逆向与防护)

一、cocos2d-x框架结构

ad7786965b7666f8a329f3aa4049ad4b.png

二、cocos2d-x引擎架构

6fde2159374badddc25348fe01cffae9.png

三、cocos2d-x源码结构

e27c19d2bf0c27ad71ae651baaaec733.png

四、cocos2d-x源码解析

1.luaLoadBuffer函数分析

cocos2d-x-3.16/cocos/scripting/lua-bindings/manual/CCLuaStack.h
cocos2d-x-3.16/cocos/scripting/lua-bindings/manual/CCLuaStack.cpp

a260f904694739566a7683e58209de03.png

2.applicationDidFinishLaunching函数分析

cocos2d-x-3.16/tools/simulator/frameworks/runtime-src/Classes/AppDelegate.h
cocos2d-x-3.16/tools/simulator/frameworks/runtime-src/Classes/AppDelegate.cpp

841fc5dfaade4488a0e0d4090ee6b9ed.png

cocos2d-x-3.16/tests/lua-tests/project/Classes/AppDelegate.h
cocos2d-x-3.16/tests/lua-tests/project/Classes/AppDelegate.cpp
cocos2d-x-3.16/templates/cpp-template-default/Classes/AppDelegate.cpp
cocos2d-x-3.16/templates/js-template-default/frameworks/runtime-src/Classes/AppDelegate.cpp
cocos2d-x-3.16/templates/lua-template-default/frameworks/runtime-src/Classes/AppDelegate.cpp
cocos2d-x-3.16/tests/cpp-empty-test/Classes/AppDelegate.cpp

五、静态分析so的特征函数

1.cocos2d-x lua的版本号

dd639f0d79f087b868a1a8d3ceb7f4e6.png

2.cocos2d-x luajit的版本号

cf886c857fa3fc3b5dd3c8ceda4c1ec6.png

3.loadChunksFromZIP函数

f93705ec93f95d5786d68b1f1c2a413b.png

4.luaLoadChunksFromZIP函数

7f184ea5acfd0a60fcb7277809067861.png

5.luaL_loadbuffer、luaL_loadbufferx、lua_dump函数(加密解密lua源码)

(1).在luaL_loadbuffer函数处获取lua源码(cocos引擎的lua加载器为cocos2dx_lua_loader,最终都是调用luaL_loadbuffer函数来加载,lua源码的加密解密等保护是通过对lua_loader为后缀相关的函数为加载器进行封装,实现对lua文件的加密/解密、压缩/解压缩等处理)

(2).在更底层的reader函数处获取lua源码(lua引擎加载lua脚本最底层是到lua_reader函数,该函数负责最底层的脚本buff遍历,因此在此处Hook dump出来的lua脚本是最纯正的lua脚本,所有加密都已经被去除(修改lua opcode或引擎逻辑除外)(该点获取不到足够的文件信息(文件名、buff index等),需要配合上层函数拼凑lua脚本)

(3).定位到luaL_loadbuffer函数,然后往上回溯,分析出解密的过程

(4).打包在App中的lua代码一般是加密过的,程序在加载lua脚本时解密(关键函数luaL_loadbuffer),解密后就能够获取lua源码。如果解密后获取的是luac字节码的话,也可以通过反编译得到lua源码,反编译主要用的工具有unluac和luadec51

718e432e5ee5b63a1904a719b707a953.png

jlicht_lua_loader
cocos2dx_lua_loader
jinit_marker_reader
bool AppDelegate::applicationDidFinishLaunching()
AppDelegate::applicationDidFinishLaunching(AppDelegate *__hidden this)
int LuaStack::luaLoadBuffer(lua_State *L, const char *chunk, int chunkSize, const char *chunkName)
cocos2d::CCLuaStack::lua_loadbuffer(lua_State , char const, int, char const*)
int luaL_loadbuffer (lua_State *L, const char *buff, size_t sz, const char *name)
cocos2d::LuaStack::luaLoadBuffer(lua_State *, char const*, int, char const*)
typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz)
typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud)

6.applicationDidFinishLaunching、tgaLoadBuffer、loadBinary、loadBreakConfig、loadBottleModel、loadBoxData、ApplicationProtocol函数

734b2831b593cb4b20161dfc4a05b841.png

7.initWithImageFile、initWithImageData、initWithImageFile等函数(加密解密手游的资源文件)

cocos2d::CCImage::initWithImageFile(char const*, cocos2d::CCImage::EImageFormat
cocos2d::CCImage::initWithImageData(void *, int, cocos2d::CCImage::EImageFormat, int, int, int)
jlicht::Image::initWithImageFile(std::string const&, bool)
lua_cocos2dx_Image_initWithImageFile(lua_State *)
cocos2d::Image::initWithImageFile(std::string const&)
int lua_cocos2dx_Image_initWithImageFile(lua_State* tolua_S)
bool initWithImageFile(const char * strPath, EImageFormat imageType = kFmtPng)
bool initWithImageData(void * pData, int nDataLen, EImageFormat eFmt = kFmtUnKnown, int nWidth = 0, int nHeight = 0, int nBitsPerComponent = 8)
bool Image::initWithImageFile(const std::string& path)
bool CCImage::initWithImageFile(const char * strPath, EImageFormat eImgFmt/* = eFmtPng*/)
bool js_cocos2dx_Image_initWithImageFile(JSContext *cx, uint32_t argc, jsval *vp)
static int tolua_Cocos2d_CCImage_initWithImageFile00(lua_State* tolua_S)
static int tolua_Cocos2d_CCImage_initWithImageData00(lua_State* tolua_S)

六、cocos2dx lua脚本的保护浅谈

1.lua、luac、luaJIT三种文件的关系

其中lua是明文代码,直接用记事本就能打开,luac是lua编译后的字节码,文件头为0x1B 0x4C 0x75 0x61 0x51,lua虚拟机能够直接解析lua和luac脚本文件,而luaJIT是另一个lua的实现版本,JIT是指Just-In-Time(即时解析运行),luaJIT相比lua和luac更加高效,文件头是0x1B 0x4C 0x4A

2.游戏厂商一般都不会直接把lua源码脚本打包到App中发布,一般对lua脚本的保护大致有下面3种,如下所示:

(1).cocos2dx lua脚本加密、在加载脚本之前解密

该方案很容易通过hook cocos的关键接口获取到lua源码

(2).将cocos2dx lua脚本编译成luaJIT字节码并且加密打包

该方案能够较好的保护lua源码,主要是先解密后反编译,反编译主要是通过luajit-decomp项目,它能够将luajit字节码反编译成伪lua代码

(3).修改cocos2dx lua虚拟机中opcode的顺序,指令抽取等

该方案主要是修改lua虚拟机源码,再通过修改过的虚拟机将lua脚本编译成luac字节码,达到保护lua源码的目的。这种情况如果直接用上面的反编译工具是不能将luac反编译的,需要在程序中分析出相对应的opcode,然后修改lua项目的opcode的顺序并重新编译生成反编译工具,才可以反编译

3.Cocos2d-x对于lua脚本加密提供了一种轻量级解决方案

七、常用的手游逆向分析工具

bamen.apk
GameKiller_v2.3.apk
GameGuardian.8.36.1.apk
Cheat Engine 6.5
NET.Reflector
ILSpy

八、参考文章

Lua程序逆向之Luac文件格式分析
https://www.anquanke.com/post/id/87006


Lua程序逆向之Luac字节码与反汇编
https://www.anquanke.com/post/id/87262


Lua程序逆向之Luajit文件格式
https://www.anquanke.com/post/id/87281

169902a206a8fcd4e165ae53227d576c.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值