cocos2dx lua 反编译(20170417增加补充说明)(转)
网上各种教程、各种工具用不了,才会有这个文章。附件是我修改的支持luajit 2.1.0-beta2反编译的LJD
lua bytecode解密
知己知彼很重要,搜索“cocos2dx lua 加密”大概可以找到类似下面的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | bool AppDelegate::applicationDidFinishLaunching() { ... CCLuaStack *pStack = pEngine->getLuaStack(); // 如果设置了 -e 和 -ek 要加上下面这句 // pStack->setXXTEAKeyAndSign("aaa", 3); // 如果设置了 -e 和 -ek -es 则要加上下面这句 pStack->setXXTEAKeyAndSign( "aaa" , 3, "XT" , 2); // load framework pStack->loadChunksFromZip( "res/framework_precompiled.zip" ); pStack->loadChunksFromZip( "res/game.zip" ); pStack->executeString( "require 'main'" ); return true ; } |
我的目标是一个ANDROID游戏,APK文件直接解压。一般情况是libcocos2dlua.so,IDA 打开,函数窗口直接搜索applicationDidFinishLaunching,就能带你飞,可惜只有loadChunksFromZip,没有setXXTEAKeyAndSign,这保存解密KEY的被编译优化了。怎么办?IDA字符串窗口帮你忙,编译器编译代码的时候都是就近原则,只要是差不多地方出现的字符串,都会被放在一起。加密的ZIP文件,文件头几个字符就是setXXTEAKeyAndSign的SIGN参数,加上ZIP文件本身的路径,搜索目标ZIP文件就可以了。如图,IDA显示附近也就几个字符串,剩下的KEY参数是哪个,一个一个试就可以了。
加密的ZIP文件:
IDA中的字符串,第一个黑块是SIGN参数,非常幸运,尝试第二个黑块就是KEY:
https://github.com/cocos2d/cocos2d-x/search?p=1&q=loadChunksFromZip&type=&utf8=%E2%9C%93 找到cocos2dx的loadChunksFromZip的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 | if (isXXTEA) { // decrypt XXTEA xxtea_long len = 0; buffer = xxtea_decrypt(bytes + stack->_xxteaSignLen, (xxtea_long)size - (xxtea_long)stack->_xxteaSignLen, (unsigned char *)stack->_xxteaKey, (xxtea_long)stack->_xxteaKeyLen, &len); zip = ZipFile::createWithBuffer(buffer, len); } else { if (size > 0) { zip = ZipFile::createWithBuffer(bytes, (unsigned long )size); } } |
这里我遇到一点困难,cocos2dx的源码项目,没有xxtea的源码!不过还是让我在github搜索出来了,源码在此:
https://github.com/xxtea/xxtea-c
直接写个C++代码,就把加密的ZIP解出来了。顺带一提,Sign就是用来跳过,跟什么PE,MZ是一样的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | HANDLE hFileOUT = CreateFile(L "e:\\tmp\\y1\\xxx\\assets\\res\\de_xxx.zip" , GENERIC_ALL, 0, 0, CREATE_ALWAYS, 0, 0); HANDLE hFile = CreateFile(L "e:\\tmp\\y1\\xxx\\assets\\res\\xxx.zip" ,GENERIC_ALL, 0,0,OPEN_EXISTING,0,0); if (hFile != INVALID_HANDLE_VALUE) { DWORD dwSize = GetFileSize(hFile, 0); size_t dwOut = 0; DWORD dwRead = 0; LPBYTE p = new BYTE [dwSize]; ReadFile(hFile, p, dwSize, &dwRead, 0); DWORD dwSignLen = 3; LPBYTE p2 = ( LPBYTE )xxtea_decrypt(( void *)(p + dwSignLen), dwSize - dwSignLen,( void *) "key" , &dwOut); WriteFile(hFileOUT, p2, dwOut, &dwRead, 0); delete p; CloseHandle(hFileOUT); CloseHandle(hFile); } |
de_xxx.zip的头两个字节变成喜闻乐见的PK,解压,搞定。
Luajit ASM
解压出来的文件,都是LuaJIT— a Just-In-Time Compiler for Lua,文件头长这样:
http://luajit.org/download.html 可以下载到LuaJIT的源码,不过兼容性有点糟糕,需要找到正确的版本才有效。回到IDA字符串窗口搜索luajit,可以确认目标APK使用的是2.1.0-beta2。下载源码,编译之后,使用类似下面的命令行:
luajit.exe -bl xxx.lua.bytecode xxx.lua.asm
可以看到LUA的ASM代码了,LUA ASM长这样:
0013 TGETV 4 4 1
0014 CALL 3 4 2
0015 ISNEXT 6 => 0019
0016 => MOV 8 7
0017 MOV 9 2
0018 CALL 8 1 2
0019 => ITERN 6 3 3
0020 ITERL 6 => 0016
0021 RET0 0 1
BYTECODE的定义在http://wiki.luajit.org/Bytecode-2.0 。不过我智商不够,看不懂,
Luajit decompiler
搜索了无数反编译文章,基本就是https://github.com/NightNord/ljd ,然而这货2014年之后就再没更新了,并不能用,没有选择,只能自己动手改。
1. 由于新Opcode导致的失败
luajit 2.1.0-beta2源码的lj_bc.h里的#define BCDEF,下方就是长长一串Opcode定义。
Ljd源码的bytecode\instructions.py 和 rawdump\code.py的两张Opcode表必须跟luajit 2.1.0-beta2 的lj_bc.h一一对比,少了的就要补上。
这里不得不吐槽一下luajit,新增Opcode其实并没有人用,Opcode是通过数组维护的,随便加个新的Opcode都会导致原有的Opcode编码改变。。。
2. 由于assert导致的失败,注释掉就好了。虽说有点不负责,但是反编译各种语法处理我也不懂。
LJD改好之后,main.py xxx.lua.bytecode > xxx.lua,终于可以看到正常的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | DeprecatedNetworkClass = {} or DeprecatedNetworkClass local function deprecatedTip(old_name, new_name) print( "\n********** \n" .. old_name .. " was deprecated please use " .. new_name .. " instead.\n**********" ) return end DeprecatedNetworkClass.WebSocket = function () deprecatedTip( "WebSocket" , "cc.WebSocket" ) return cc.WebSocket end _G.WebSocket = DeprecatedNetworkClass.WebSocket() return |
完毕!
20170417补充说明:
请大家先确认自己的目标,再使用代码。如果版本不一致,你们需要先对比luajit源码中的lj_bc.h和https://github.com/NightNord/ljd的bytecode\instructions.py 和 rawdump\code.py中的OPCODE表,对得上,才能正确反编译,对不上就自己参照着来改。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)