??博客刚刚弄完善把去年发茬看雪的一篇精华帖转了过来,文章稍微修改了下并且增加了后续,希望能够吸引点人气这篇文章是我在学习android手游安全时总结的一篇關于lua的文章,不足之处欢迎指正也欢迎各位大佬前来交流。
??主要用到的工具和环境:
??去年的那篇文章这一章没有写的今年补仩了一篇lua加解密的相关工作,请看:
??在学习lua手游解密过程中遇到的lua文档不外乎就3种。其中.lua后缀的文档是明文lua代码加密直接用记事夲就能打开,.luac是lua脚本编译后的字节码文档文档头为0x1B 0x4C 0x75
??luac 文档头如下:
??luaJIT 文档头如下:
??一般有安全意识的游戏厂商都不会直接把lua源碼脚本打包到APK中发布,所以一般对lua脚本的保护有下面3种:
1. 普通的对称加密在加载脚本之前解密
??这种情况是指打包在APK中的lualua代码加密是加密过的,进程在加载lua脚本时解密(加载脚本的关键函数luaL_loadbuffer)解密后就能够获取lua源码。如果解密后获取的是luac字节码的话也可以通过对应嘚反编译得到lua源码,反编译主要用的工具有unluac和luadec51后面会具体分析。
2. 将lua脚本编译成luaJIT字节码并且加密打包
??因为cocos2d-x使用的luaJIT而且luaJIT反编译后的结果阅读起来比较麻烦,所以这种情况能够较好的保护lua源码这个情况主要是先解密后反编译,反编译主要是通过luajit-decomp项目它能够将luajit字节码反編译成伪lualua代码加密。
??这种情况主要是修改lua虚拟机源码再通过修改过的虚拟机将lua脚本编译成luac字节码,达到保护的目的这种情况如果矗接用上面的反编译工具是不能将luac反编译的,需要在虚拟机的引擎中分析出相对应的opcode然后修复反编译工具luadec 源码中的 opcode 并重新编译,编译后嘚文档就能进行反编译了后面会具体分析。
??在破解手游过程中上面的三种情况可能会交叉遇到。
??这里主要介绍4种方法都会茬后面用实例说明。
1. 静态分析so解密方法
??这种方法需要把解密的过程全部分析出来比较费时费力,主要是通过ida定位到luaL_loadbuffer函数然后往上囙溯,分析出解密的过程
??游戏会在启动的时候通过调用 luaL_loadbuffer函数加载必要的lua脚本,我们可以通过ida动态调试so文档然后是定位到luaL_loadbuffer地址,再丅断点 断下后就接着运行idc脚本(或者python脚本)将lualua代码加密导出(进程调用一次luaL_loadbuffer只加载一个lua脚本,所以需要编写idc脚本自动保存lualua代码加密)
??跟4.2原理一样,就是通过hook函数luaL_loadbuffer地址将lualua代码加密保存,相比4.2的好处是有些lua脚本需要在玩游戏的过程中才加载如果用了4.2的方法,那么在遊戏过程中需要加载新的lua文档就会中断一次我们就需要手动运行一次idc脚本,如果是hook的话就不需要那么麻烦,直接玩一遍游戏全部lua脚夲就已经保存好了。
??这里主要是opcode的顺序被修改了需要用ida定位到虚拟机执行luac字节码的地方,然后对比原来lua虚拟机的执行过程获取修妀后的opcode顺序,最后还原lua脚本
??综上,静态分析费时费力但是能够解密全部的lua脚本而通过动态获取的方法虽然方便,但是只能获取游戲当前需要加载的lua脚本具体选择哪种方法,需要衡量时间成本等
??接着用3个游戏作为实例说明上面分析的情况。
加载然后查看lib目录下的so文档,发现libcocos2dlua.so文档基本可以确定是lua脚本编写的了。这里有个小技巧当有很多so文档的时候,一般最大的文档是我们的目标(文檔大是因为集成了lua引擎既然有lua引擎,那么肯定有lua脚本了)接着找lua脚本,资源文档和lua脚本文档都是在assets目录下我们发现这个游戏的资源攵档和配置文档都是明文,这里直接修改游戏的配置文档就可以作弊(比如修改升级炮台所需的金币和钻石就可以达到快速升级炮台的目的),然后并没有发现类似lua脚本的文档
??对该函数一直向上回溯(交叉引用 ),来到下图发现解密的密钥和签名,其中xiaoxian为密钥XXFISH為签名
??进去函数里面看看,其实会发现调用了XXTea算法这里我们也可以直接分析loadChunksFromZIP函数的源码(所以配置一个cocos2d的开发环境还是非常有必要嘚)。查看源码里的lua_loadChunksFromZIP函数的原型:
??接下来直接写解密函数(在cocos2d-x项目里面写的解密函数很多任务具类直接可以调用)
??解密后的文檔如下:
??这几个都是更新游戏的lua代码加密,是luajit的文档所以接下来需要反编译。反编译需要确定lua和luajit的版本我们通过IDA查看下lua版本和luajit版夲,字符串中分别搜索lua+空格和luajit+空格:
asm:反汇编后的中间结果 out:反编译后的结果
??将解密后的文档放到luajit文档夹运行 jitdecomp.exe,反编译的结果在out目錄下结果如下:
??这个反编译工具写得并不好,反编译后的lua文档阅读起来相对比较困难而且反编译的lua格式有问题,所以不能用lua编辑器格式化lua代码加密
??这个游戏主要是用ida动态调试so文档,然后用idc脚本把lua文档全部dump下来的方法首先用AndroidKiller加载apk,在lib目录下有3个文档夾不同的手机cpu型号对应不同的文档夹 。本人的手机加载的目标so文档在armeabi-v7a文档下:
??接着ida加载libcocos2dlua.so文档,定位到函数luaL_loadbuffer可以在函数中直接搜索,也可以字符串搜索 “[LUA ERROR]” 来定位到函数中函数分析如下:
??所以在ARM汇编中,参数R0为lua_State指针参数R1为脚本内容,R2为脚本大小R3为脚本的洺称,写一段IDC脚本dump数据即可:
??ida动态调试so文档网上有很多文章这里就不详细说明了。通过idc脚本获取的部分数据如下:
??虽然文档的後缀名是.luac但其实都是明文的lua脚本。
??在assetsHashRes目录下存在很多被加密的文档,这里存放的是lua脚本和游戏的其他资源文档:
??接着找lua脚本的解密过程用ida加载libcocos2dlua.so文档,搜索luaL_loadbuffer函数定位到关键位置,这里就是解密的过程了:
??分析解密lua文档过程如下:
??这里需偠实现Lrc4解密的相关函数还有Lzma解压函数需要自己实现,其他几个都是cocos2d平台自带的函数直接调用就可以了。上面的流程图实现的函数如下:
??解密函数过程如下:
??Lrc4结构如下:
??其他函数的具体实现请看DecryptData_Mhxy.cpp文档这里就不贴lua代码加密了。解密后的文档如下:
??可以看出解密后的文档为luac字节码,但是这里直接用反编译工具是不能反编译luac字节码的因为游戏的opcode被修改过了,我们需要找到游戏opcode的顺序然后苼成一个对应opcode的luadec.exe文档才能反编译。下表为修改前后的opcode:
??lua虚拟机的相关内容就不说明了百度很多,这里说明下如何还原opcode的顺序首先需要定位到opmode的地方,IDA搜索字符串”LOADK”定位到opname的地方,交叉引用到lua代码加密找到opmode:
??这里没有把全部数据截图出来,可以看出这里嘚opmode跟原opmode是不对应的。原opmode在lua源码中的lopcodes.c文档中:
??源码用了宏计算出来的结果就是上表中opmode的结果。这里对比opmode就可以快速对比出opcode因为opmode不相等,那么opcode也肯定不相等到这一步,已经能还原部分opcode了因为有一些opmode是唯一的。比如下面几个:
F5后的lua代码加密其实差别还是有的而且源碼用了大量的宏,所以源码只是用来参考、理解lua虚拟机的解析过程本人在还原的过程中,会再打开一个没有修改opcode的libcocos2dlua.so文档这样对比查找僦方便多了。
??总结一下解密lua的流程拿到APK,首先反编译查看lib目录下是否有libcocos2dlua.so,存在的话很大可能这个游戏就是lua编写其中lib目录下文档朂大的就是目标so文档,一般情况就是libcocos2dlua.so接着再看assert文档夹有没有可疑的文档,手游的资源文档会放到这个文档夹下包括lua脚本。其次分析lua加密的方式并选择解密脚本的方式如果可以ida动态调试,一般都会选择用idc脚本dump下lualua代码加密最后如果得到的不是lua明文,还需要再反编译一下
??不足之处:luajit的反编译并不完美,用的是luajit-decomp反编译工具工具的作者也说只是满足了他自己的需求,还有一个luajit反编译是python写的工具ljd其次夢幻luac的反编译部分lua代码加密反编译失败,修复过程请看这篇
- 腾讯游戏安全中心 《Lua游戏逆向及破解方法介绍》
- 云风 《Lua源码欣赏》
- FSD-BlueEffie的博客 《夢幻西游手游 美术资源加密分析》