目前手游游戏所用的引擎,常见的框架有cocos2d,Unity3d,unrealengine等。Lua由于其简单的语言结构和语法,用于很多游戏引擎。
这次以某常见三消游戏为例,来进行逆向工作。
文件分析
确定分析对象
解压apk安装包,进入./lib目录,都是.so的底层库。
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
| lib39285EFA.so //移动安全联盟 OAID SDK 广告服务等 libsgcore.so //快手 libBugly.so //腾讯Bugly crash监控 libhegame.so //游戏逻辑 libtobEmbedEncrypt.so //bytedance广告 libCtaApiLib.so //极光JVerification Android SDK libtquic.so //quic协议? libShanYCore.so //闪验Flutter libiconv.so //多语言编码 libtraeimp-rtmp.so //腾讯云移动直播 libbuffer_pg.so //字节 libindoor.so //百度室内定位 libtxffmpeg.so //ffmpeg libc++_shared.so libkwad_common.so libtxplayer.so //腾讯云短视频 libcostquic.so //quic协议? libliteavsdk.so //移动直播sdk libtxsdl.so //腾讯sdl? libfile_lock_pg.so liblocSDK7b.so //百度定位? libweibosdkcore.so //微博 libmarsstn.so //微信Mars 跨平台基础架构组件 libzbarjni.so //Zbar读取条码 libmarsxlog.so //微信Mars 跨平台基础架构组件 libnms.so //对象检测
|
用ida打开可疑的libhegame.so 看到函数的名字有大量的lua ,可以知道开心消消乐使用的lua引擎(和网上搜索得到的信息一致)
assert/src 目录下是lua脚本,有经过加密,需要对.so文件进行分析找到密钥。
分析libhegame.so
方法名里搜索“lua”,可以看到都是jni的方法。
查看j_Java_com_happyelements_test_TestService_initLua
方法,一路跟进sub_6EA84
。
可以看到初始化lua的方法应该是com/happyelements/android/LuaHelper
类里的initLuaEnv()
方法.
全文搜索“load_lua”,可以看到方法sub_106B0C()
,根据调试信息可以看出此方法为加载lua文件的地方。
其中sub_569864
为读取lua文件,sub_95C00
为解码,sub_97670
为解压缩
sub_95C00 -> sub_95C18
从调试信息 推测sub_95C18
为openssl的EVP解码算法。
动态调试
frida hook sub_95C00方法
js脚本内容为:
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
| function get_func_addr(module, offset) { var base_addr = Module.findBaseAddress(module); console.log("base_addr: " + base_addr); console.log(hexdump(ptr(base_addr), { length: 16, header: true, ansi: true })) var func_addr = base_addr.add(offset); if (Process.arch == 'arm') return func_addr.add(1); //如果是32位地址+1 else return func_addr; } //var func_addr = get_func_addr('libhegame.so', 0x97670); var func_addr = get_func_addr('libhegame.so', 0x95C00); console.log('func_addr: ' + func_addr); console.log(hexdump(ptr(func_addr), { length: 16, header: true, ansi: true })) Interceptor.attach(ptr(func_addr), { onEnter: function(args) { console.log("onEnter"); var num1 = args[0]; var num2 = args[1]; console.log("num1: " + num1); console.log("num2: " + num2);
console.log(hexdump(ptr(num1), { length: 16, header: true, ansi: true })) }, onLeave: function(retval) { console.log("onLeave"); //retval.replace(3); //返回值替换成3 console.log("return: " + retval);
} });
|
可以看到密钥为e9 74 7d 92 cc 32 2e 7d 11 2e 7c 34 51 d7 b3 6a
解码lua文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #coding=UTF-8 import Crypto import zlib from Crypto.Cipher import AES def decdata(c): key=b'\xe9\x74\x7d\x92\xcc\x32\x2e\x7d\x11\x2e\x7c\x34\x51\xd7\xb3\x6a' iv=c[0:16] main_data=c[16:] cryptor = AES.new(key,AES.MODE_CBC,iv) pad_compress_data=cryptor.decrypt(main_data) str_len=len(pad_compress_data) pad=pad_compress_data[-1] compress_data=pad_compress_data[0:str_len-pad] plain_text = zlib.decompress(compress_data) return plain_text
|