【原创】IOS逆向实战1.1之我与cocos2d-x的全过程(入门推荐+读完可破90%以上的手游)

目录(可键入索引加条目Ctrl+F自行搜索跳转):

  1. 前言
  2. 准备工作
    1.1 获取目标程序资源
  3. 逆向分析
    2.1. 静态分析
    2.1.1 确认游戏引擎
    2.1.2 了解coco2d-x的载入步骤
    2.2. 动态调试
    2.2.1 远程动态调试配置
    2.2.2 动态调试获取密钥
  4. 解密脚本
    3.1 无法解密?
    3.2 发现与还原算法
  5. Lua反编译
    4.1 初步了解Lua编译器
    4.2 深入LuaJit
    4.3 反编译脚本字节码
    4.4 重新编译脚本
  6. 真机调试
    5.1 越狱机运行
    5.2 非越狱机运行

0.前言:
作者写这篇文章的原因是因为网上关于IOS平台下逆向cocos2d-x的文章比较匮乏,希望各位对逆向感兴趣的同道读后能节省很多查资料的时间,也让初学者对逆向不再感到神秘;如果对cocos这个引擎感兴趣的也可以先读读这篇文章。注:本文章只用于学习用途,任何商业形式的衍生作品与作者没有任何关系!转载请注明出处!

1.准备工作:
这一次我们准备逆向的游戏是《烟雨江湖》(国际服),感兴趣的朋友可以到美国地区的AppleStore下载,并且砸掉ipa的壳,关于如何砸壳我们在这里不多介绍了。如果是初学逆向的同道可以阅读我的另一篇文章 《IOS逆向1.0之环境配置与砸壳IPA》;本文中使用的所有工具也都会在这里介绍和提供安装教程!

1.1.获取目标程序资源:

我们将砸壳(我用的是Clutch砸的壳,Frida砸这款游戏莫名死机不知道为什么,也懒得去排查,等新版本吧 :see_no_evil:)后的ipa利用ssh的方式从手机中复制到电脑,手机的IP地址要到手机上打开Wifi选择蓝色的感叹号IPv4地址中的第二个,打开CMD依次输入(<>号不保留;括号内的是提示,不要输入):

scp <root@<你越狱手机的IP>:手机砸壳后的路径(clutch默认路径:/var/tmp/clutch/)> <导出的路径>

<密码>(默认密码: alpine)

导出后我们来到导出的路径将.ipa改成.zip并解压

clutch_dumped

解压后我们进入解压好的目录打开他们的资源文件查看一下,发现它们有一个config.json那我们打开了看一下,发现入口文件是main.lua脚本


那我们就找到该脚本,打开src_encrypt64/main.lua看到这个脚本已经被加密了。但当我们打开了其它的脚本文件后,发现所有的加密文档里都有一个统一的明文字段”BABBY“,那我们就可以大概确定这个加密算法应该就是cocos2d-x的xxtea了。

2.逆向分析:
逆向分析的过程往往伴随着失败与不解,如果你在某环节出现了疑惑或困扰。请记住,一定是你的姿势错了,虽然反复的尝试会逐渐的感到很枯燥,但只要你对逆向感兴趣且抱有成功的信心,那你离成功不会太远了!

2.1静态分析
首先我们先打开 IDA Pro 64bits 版本,为什么要打开64bit版本呢?因为IOS用的是ARM64指令集所以我们的反汇编工具也必须要选择可以支持64bits的才行。

IDA的截图

打开后(如果你是已经注册了的)选择OK->New->取消->将com.novastar.yanyu.ios/JiangHu-mobile拖入IDA Pro->选择Fat Mach-O file, 2.ARM64->Ok->Yes->等待IDA分析完(大约30分钟)


在逆向中,字符串总会是最佳的切入点,所以等到IDA分析完函数后我们还需要分析字符串列表。按下Ctrl + 1, 选择Strings->OK(大约几个小时)

2.1.1确认游戏引擎
刚才我们猜测了这个游戏的引擎是用cocos2d-x开发的,但我们还不能确认,所以我们现在需要去确认一下。按照刚才打开字符串的方式打开IDA的字符串窗口,并按下Ctrl+F搜索热键,输入:cocos2d-x,然后发现的确有匹配字段,那我们就能确定这款游戏是用cocos2d-x引擎开发的了。

2.1.2了解coco2d-x的载入步骤
当我们在确认一个游戏是由什么引擎开发之后,如果是开源引擎的话,我们就需要去了解这款开源引擎。点击coco2d-x进入该引擎的github连接。点击Code->复制Clone的HTTPS连接
cocos2dx_github_clone

打开CMD输入下面的指令(<>号不保留;括号内的是提示,不要输入):

cd /d <导出的IPA的路径>
git clone https://github.com/cocos2d/cocos2d-x.git
cd cocos2d-x
git tag (检查是否有版本cocos2d-x-3.8.1)
(一直回车到<END位置>)
git checkout cocos2d-x-3.8.1  (cocos2d-x的相关安装请阅读我的另一篇文章《IOS逆向1.0之环境配置与砸壳IPA》)
cocos new JiangHu -p com.lilpoppy.Jianghu -l lua (之后我们来到Jianghu这个文件夹内就能看到有一个config.json文件了)

然后我们打开Visual Studio->打开本地文件夹->选择JiangHu这个文件夹->frameworks->runtime-src->proj.win32->右键JiangHu.sln->打开

打开项目后我们再来到Classes->AppDelegate.h 就能看到applicationDidFinishLaunching这个启动函数了,然后我们按住Ctrl点击这个函数进到它的cpp实现来查看它的载入步骤。我们先把刚才发现的那个加密的Sign放到stack->setXXTEAKeyAndSign里,然后画红线的地方就是我们这次要逆向的目标函数了。

既然确认了目标函数,那我们就回到IDA,还是老方法先从Strings下手。键入我们看到的Sign ”BABBY“,却发现并没有该字符串。

不要紧,那我们搜索一下”applicationDidFinishLaunching“,发现能搜索到。

我们双击点进去,按X会发现它有5个xrefs

我们依次点进去看看这些地址会不会导入到我们需要的函数地址,却发现这些地址都没有xref

看来这个游戏有做相应的防护处理,似乎线索到这里已经断了?其实并没有,我们还有两条画了红线的函数还没有尝试setXXTEAKeyAndSign 和 executeScriptFile,那我们就依次把这两个函数在Strings和Functions窗口(Ctrl + 1 后选择Functions来打开搜索热键也还是Ctrl+F)搜索,可结果还是没有。嗯,其实已经预料到了,既然applicationDidFinishLaunching函数被处理了,那这两个函数没有突破口也没什么稀奇。但我们还有一个字段可以去尝试那就是"main.lua",在字符串窗口里搜索”main.lua“之后老方法按x查看它的xrefs,惊喜的发现这里有个地址跳到了sub_1000A32CC+109C,很好这就是我们想要的!(为什么这个他们不隐藏呢 :face_with_raised_eyebrow:??)那我们就点OK进入到那个地址。

嗯,找到了入口函数之后我们按F5启用IDA的hex-ray伪C代码映射,我们拉到最上面,按N键给这个函数命名”cocos2dx__applicationDidFinishLaunching“,然后点OK。

既然获取了函数,那我们就快点去找它的key来解密这个游戏的.lua脚本实现自定义游戏吧 :crazy_face:!嗯根据我们的源码部分我们已经知道了我们只需要找到

virtual void setXXTEAKeyAndSign(const char *key, int keyLen, const char *sign, int signLen)

就可以获取这个游戏的脚本密钥(总结下为什么网上其它教程都说搜索Sign的话Key就会在附近,因为cocos2d-x的这个函数就是这样用的)。那我们就赶紧阅读下这个源码吧!

setXXTeaKeyAndSign

嗯,经过一番阅读之后,我们并没有发现有函数长这样sub_xxxxxxxx(KEY, KEY_LEN,”BABBY“, SIGN_LEN),这也就是为什么我们之前搜索它的Sign的时候找不到的原因了,但我们却发现了它们用算法来生成密钥和Sign,嗯有点安全意识??为什么不隐藏main.lua呢?

从上图(代码太长我就不截图了,有兴趣的可以下载游戏一步步来学习)我们可以发现它们应该是用sub_1000A3230(v100, “B”)来分配了一个内存地址给std::string (指针)v100并写入”B“,然后下面Label_230的位置又调用了std::string::append(&98, “A”, 1L) 来给这个地址来加入一个const char,而且其它地方也都有零零散散的BABBY的其它字母出现也就是说我们可以在动态调试的时候获取它的Sign和Key,那我们先在这个地址的IDA View中下个断点吧。首先我们来到它的开始的地方也就是sub_1000A3230(v100, “B”)这个方法这里,鼠标单击这个方法并按下X查看它xrefs,并记下它的地址,也就是cocos2dx__applicationDidFinishLaunching+CD8

然后我们回到IDA View-A再按下G键并在Jump address的输入框中输入cocos2dx__applicationDidFinishLaunching+CD8,OK,然后在这个地方按下F2添加断点。**

然后再按空格进入Graph View按住Ctrl+鼠标下滚缩小,单击选择这个函数的开始位置按F2下个断点。可以按Ctrl+Alt+B来查看断点是否成功下好**

2.2. 动态调试
有静就有动,静态分析是把程序的字节码转换成对应的命令集从而进行分析。而动态调试就是在程序运行时我们可以对其运行路线与结果进行干涉和分析。

2.2.1 远程动态调试配置
IOS的远程调试需要利用到debugserver,而debugserver只有在设备连接过一次Xcode(安装详情请参考《IOS逆向1.0之环境配置与砸壳IPA》),并在window->Deveice菜单中添加此设备后,debugserver才会被Xcode安装到IOS的“/Develope/usr/bin”目录下,而这一个debugserver这时候还不能调试其它app的,我们还需要利用ldid来对它重新签名(安装详情请参考《IOS逆向1.0之环境配置与砸壳IPA》)。

首先我们在IDA的菜单栏找到并依次进入Debugger->SwitchDebugger 选择Remote iOS debugger然后点击确定

然后再到IDA的菜单栏中依次进入Debugger->Debugger options 将 Logging 中的 Thread start/exit、Library load/unload、Breakpoint 和 Debugging Message选中,然后再点击进入Set specific options

在iOS configuration对话框中点击Symbol path中的 … 来为debugger加入符号(也可以不加,但你每次调试都要等很久)。

image

(详情请阅读《IOS逆向1.0之环境配置与砸壳IPA》)Symbol path你可以打开VMWare虚拟机,确保你下有xcode且用xcode连接该手机并调试过的情况下,打开Finder->按cmd + shift + g键入

/Users/lilpoppy/Library/Developer/Xcode/iOS DeviceSupport

后回车,选择你越狱手机的版本号,在VMWare虚拟机装有VMTools的情况下直接把文件拉到我们的导出路径中。

IDA导入后的样子是

点击OK->OK后,我们再来到Debugger->Processes options,然后将其Application的路径设为移动端的游戏路径,获取路径的方式如下:

1.在越狱手机打开游戏

2.打开CMD并键入(<>号不保留括号为注释)

ssh root@<越狱手机的IP>(有时候连不上去就关掉手机的网络再重启就好了)
<密码>(默认密码为alpine)
ps -e

找到游戏的进程,并复制其路径

然后Input file填我们目前IDA打开的这个项目,Hostname填越狱手机的IP,port填debugserver开的端口。并将Save network settings as default 勾起来,这样我们不用每次都再填写IP地址和端口了。
ida_process_options

到这里为止我们的IOS Remote Debugger已经配置好了,现在我们需要测试一下远程调试是否工作

打开CMD依次输入(<>号不保留,括号为注释)

电脑IP地址可以输入 ipconfig /all 来查看

ssh root@<越狱手机IP地址>
<密码>(默认密码为alpine)
debugserver <电脑IP地址>:<端口(2222)>

如果显示的如下图就说明你现在可以远程调试了!

debugserver_installed

现在我们先去手机上打开游戏,然后重新再打开一个cmd依次输入如下命令。(<>号不保留括号为注释)

ssh root@<越狱手机IP地址>
<输入密码> (默认密码为alpine)
ps -e

找到烟雨江湖的进程后,那我们再在IDA中依次打开Debugger->Attach 在 处双击,然后会弹出另一个对话框"Please enter a number",在Input输入框中填入你用ps -e找到的游戏进程id,再点OK。

ida_attach_process

然后我们就成功的附加入进程了!

2.2.2 动态调试获取密钥

而我们想要在入口函数下触发断点肯定就不能用attach process来进行,因为附加进程肯定会错过我们想要调试的函数,所以我们需要使用另外一种方式,启动进程:F9。

首先我们需要先回到CMD启动debugserver键入debugserver <电脑ip>:<端口>(这里我就不再赘述了,如果还不懂的话请自行翻回上一步去看吧),然后按下F9,游戏正常运行,并击中我们留下的第一个断点。

这个断点只是留下来为了调试函数时更清晰步骤而准备的,而我们这次要分析的是v100(X22),所以我们按F9继续运行。按下F9之后我们来到了下一个断点。

好的,现在我们需要再下一个内存断点,看看这个内存位置是不是如我们所料的那般,每次都会写入BABBY中的一个字符。双击上面X22寄存器来到该内存地址,F2添加内存勾上:Enable、Hardware、Write、Break、Refresh debugger memory,然后OK

然后再F9运行,提示击中硬件断点,点OK,双击下面输出栏地址中的dest(: hit hardware breakpoint → ),跳转到该地址后我们发现,第一个字母变成了B。

那我们再按4下F9,为什么是4下?因为BABBY是5个字母,但我们刚才已经按了1下!然后我们再看看这个断点5次后的结果。

嗯,的确已经出了完整的sign,这说明我们之前的判断没有错。那我们现在已经肯定了这个方法一定会生成完整的密钥和Sign。但似乎上下查看了下并没有看到密钥的痕迹,那说明密钥可能是在Sign的后面生成的,那我们再按1下看看情况吧。

嗯,再按了一下后原本的sign不见了,但上面似乎出现了新的字符串。这可能就是这款游戏的脚本key,但因为我们断的不是该内存地址,所以并不能确定该密钥的完整性。那我们可以选择多下个断点来捕获密钥。首先回到IDA-View空格进入GraphView,以之前下的cocos2dx__applicationDidFinishLaunching+CD8这个断点的block为中心点按住Ctrl+鼠标滚动下滑缩小整个视图。鼠标拖动视图往下稍微拉点,找出一个运行结果点,点击那个block的顶部(你也可以Ctrl+鼠标滚动上滑放大)按F2下断点。

下好断点后我们再按Ctrl+Alt+B打开断点列表把之前下的那个内存区域的硬件断点删除掉(程序每次运行的内存地址都会不一样的),然后Ctrl+F2终止进程,再到CMD重新运行debugserver再回到IDA按下F9开始新的进程。开始进程后会提示我们触发了第一个断点,第一个断点是我们给这个方法下的断点,没什么用,再按一次F9来到赋值B到X22的那个位置,再双击X22像刚才那样重新再设置一个硬件断点。再运行4次,回到我们刚才那个地方。然后我们这次再运行一次看看这次断的顺序吧。

嗯,从输出栏看我们之前按的第6次已经超出了这个函数或者运行到了清理内存的函数。而这次我们在这个函数的结果部位下了断点已经成功的拿到了这个游戏脚本的解密密钥。至于为什么我们第三个断点的地方是这个这个函数的结果部位?你可以回到IDA并打开断点列表(这里不再赘述断点列表如何打开),双击进入我们第三个断点的位置,然后按F5,会得到下图。

看到下面的

(*(void (__fastcall **)(_QWORD *, const char *))(*v55 + 56LL))(v55, "main.lua");

就是我们之前搜索main.lua进入的地方了,再结合我们从cocos2d-x的源码

LuaStack* stack = engine->getLuaStack();
stack->setXXTEAKeyAndSign("2dxLua", strlen("2dxLua"), "BABBY", strlen("BABBY"));

//register custom function
//LuaStack* stack = engine->getLuaStack();
//register_custom_function(stack->getLuaState());

#if (COCOS2D_DEBUG > 0) && (CC_CODE_IDE_DEBUG_SUPPORT > 0)
// NOTE:Please don't remove this call if you want to debug with Cocos Code IDE
auto runtimeEngine = RuntimeEngine::getInstance();
runtimeEngine->addRuntime(RuntimeLuaImpl::create(), kRuntimeEngineLua);
runtimeEngine->start();
#else
if (engine->executeScriptFile("src/main.lua"))
{
    return false;
}
#endif

设置密钥肯定是在载入main.lua的前面的,所以这个LanJingNo1就是我们需要的密钥,现在回到IDA多行选择密钥的地址,然后按A,把它变成一个字符串,复制粘贴到我们的AppDelegate.cpp中的key的那部分吧

3.解密脚本
如果这个不是一个开源的引擎,那我们还得还原算法。但cocos2d-x是个开源的引擎,所以有了密钥就可以解密脚本了!由于我们只是想解密他们的脚本,所以编译coco2d-x就有点繁琐了(好吧,我还没用过cocos2dx开发 :see_no_evil:),所以我们在这里就自己创建一个解密工具吧。

首先打开Visual Studio->创建新项目->控制台应用->下一步->项目名称(位置你可以放到导出ipa的路径里)-> 创建

创建完成后我们去到cocos2d-x\external\xxtea的目录下,把里面的文件复制粘贴到我们项目的文件夹内。

xxtea_files

然后回到Visual Studio右键点击项目->添加->现有项->多选我们刚才复制的那两个文件->添加

在创建好我们的项目之后我们就需要开始写解密的脚本了,下面是我写的脚本大致模仿了coco2dx的解密过程并且配有输出。

3.1 无法解密?
嗯,让我们回到Visual Studio 接着按下F5,调试程序,查看输出。读取文件正常、去sign结果正常,解密结果????失败???一脸的黑人问号…

遇到这种情况,先检查好自己的代码没错的情况下,那么原因只有一种…算法不对。而我们用的cocos2dx版本是和他们的一样的,难道是他们放了个烟雾弹特地拿一个假的版本来混淆我们的视线?好吧,不管怎么样我们都还要去检查他们的算法。

首先我们去看看cocosx2-d里调用xxTea算法的地方在哪里,然后我们才能更清晰的去逆向。首先回到我们之前用Visual Studio打开的那个JiangHu.sln项目,在AppDelegate.cpp里Ctrl+鼠标移动到那两个画红线的函数位置,单击点进去。

stack->setXXTEAKeyAndSign("LanJingNo1", strlen("LanJingNo1"), "BABBY", strlen("BABBY")); 这个函数转到了CCLuaStack.h
engine->executeScriptFile("src/main.lua") 这个函数转到了CCScriptSupport.h

那我们先在”解决方案资源管理器“中搜索CCLuaStack

然后来到这个函数的实现中查看,却并没有发现有关解读文件的方法。

vs_stack_func1

那我们再去另一个源文件CCScriptSupport去找另一个函数的方法,却发现executeScriptFile在这个源文件中没有定义!那我们回去再查看一下,发现这个方法是用engine的指针call出来的,而engine这个变量又是用auto来定义的,那应该是解析失败跳转错误了。我们直接在资源管理器中搜索engine吧。

找到了后,双击打开,来到这个函数
int ret = _stack->executeScriptFile(filename);
再点进去(Ctrl+鼠标左键),发现又回到了CCLuaStack.h,那我们再回到CCLuaStack.cpp去搜索这个函数,然后根据自己的判断(不多解释了)一路点进去,最后来到了这个函数

找到解密过程那就好办了,我们只需要一路跟进去,因为引擎是开源的,所以我们只需要对照算法就行

3.2 发现与还原算法

知道了解密脚本的流程后我们就可以开始动手去复查算法了。首先回到IDA PRO老办法先从Strings和Functions下手,直接搜索luaLoadBuffer却没有搜索出结果,意料之中;如果搜索出来那又可以节省大量时间,这就是为什么我都会提醒你们不妨搜索一下,万一有意料之外的收获呢?

知己知彼放能百战不殆,其实xxtea算法还有一个常量值,就是它的Delta值 #0x9e3779b9 我们不妨也去搜索一下还可以抽空去搞卫生、洗个澡什么的。首先回到IDA,切换到IDA-View 菜单栏打开->Search->Immediate Value(Alt+I)->输入常量值->OK

接着等待5分钟后结果出来了

结果有点多,我们该如何排查呢?很简单,我们现在在每一个结果中都下一个断点。双击结果点进去按F2,然后回去重复操作。然后打开断点列表把我们之前下的内存位置的断点删掉。

因为我们之前从源码地方可以看出,我们是从

if (engine->executeScriptFile("src/main.lua"))
{
    return false;
}

这个方法进入解密过程的,所以我们再到字符串栏里搜索main.lua,从结果中双击跳转进去再按X双击结果跳转进去在此处再下一个断点。

断点设置好了,去到CMD启动debugserver,回到IDA 按 F9。接触了第一个断点

发现它不是在我们的cocos2dx__applicationDidFinishLaunching这个入口断点,按F2将它删除,然后再按F9,就来到了我们下的cocos2d-x初始化函数的断点。然后再按3下F9来到我们断下的executeScriptFile** 函数这里。**

然后在这里再按一次F9看看接下来会断到哪里,那里就很有可能是我们需要分析的加密算法的地方了!

现在按下F5打开hex-ray,然后再打开Visual Studio打开我们的JiangHuDecryptor项目,去到xxtea.cpp,找到do_xxtea_decrypt比对一下两边的算法吧。

由于IDA这边的都是伪C代码,那如何比对更清晰效率呢?我们可以尝试着把IDA这边的函数、变量、参数名翻译成我们源码这边相同的名字,命名热键还是按N,让我们行动吧!

命名好后的方法使我们更容易进行逆向工作,当然既然是伪C代码就有它的弊端,做不到和源代码一模一样。既然能确定这个函数就是解密算法了后,我们先把之前下的那些不确定的断点都删除掉吧,来到断点列表->多选除了cocos2dx__applicationDidFinishLaunching之外的->鼠标右键->Delete

回到解密函数并给它命名coco2dx__do_xxtea_decrypt然后去到IDA-View按G并键入coco2dx__do_xxtea_decrypt后按OK,再按F2给它下个断点。

既然找到了开始和结束位置我们也顺带把它们中间的过程也一起还原了吧,单击上面命名的coco2dx_do_xxtea_decrypt后按X打开它的xrefs,先从第一个开始,双击点进第一个,按F5开始命名。

然后在这个方法照上面的步骤同样也下个断点,并按X打开它的xrefs继续往上走直到找到loadBuffer为止。

然而经过我们查看了它所有的xrefs之后居然没有看到loadBuffer相似的函数,这说明这个游戏在调用loadBuffer和调用coco2dx_xxtea_decrypt之间还有一个中间函数。那我们应该怎么办?老办法,给它的xrefs都下个断点!

然后结束进程,启动debugserver,F9直到
__text:0000000101184368 ADRL X1, aMainLua ; "main.lua"
然后再按下F9来到解密函数之前的这个函数,同样给它的变量方法命名,但在命名后我们发现这个函数在解密之前居然对我们的data(在我们机器运行的就是我们的!) 进行了指针赋值操作!

那就让我们去还原它吧!

这一层解密算法很简单(外包写的吧?),还原之后我们再运行一次看看结果,发现解密算法可以成功解密了!

4.Lua反编译
经过我们的一番幸苦的(图文贴真不好写!)逆向,终于是拿到了这个游戏脚本的字节码。为什么是字节码而不应该是明文代码吗?字节码怎么转换成源码呢?那现在请跟我一起将这些字节码反编译成源码吧!

4.1 初步了解Lua编译器
我们得到的脚本是字节码的原因是因为这些脚本都已经被编译过了。而Lua编译器常用的有两种(不写lua大神勿喷):luac 和 lua jit。那我们应该怎么分辨出这个脚本是用luac还是lua jit编译的呢?其实很简单,就是看它的文件头!

Luac的signature是4个字节,而LuaJit的signature是3个字节,下面我们来对比下相同的文件中luac和luajit的文件签名吧,左边的是luac的右边是lua jit的

4.2 深入LuaJit
由于这次我们反编译的对象是用LuaJit编译的,那我们就只说LuaJit了,对Luac有兴趣的朋友可以看看虫大的帖子《Lua程序逆向之Luac文件格式分析》

关于LuaJit其实也没什么好说的我们只要了解一下它文件头的格式就好,而它文件头也已经被人分析得彻彻底底了有兴趣的可以读下这篇《 Lua程序逆向之Luajit文件格式》

而下面则是它的文件头格式

typedef struct {
    char signature[3];
    uchar version;
    GlobalHeaderFlags flags;
    if (!is_stripped) {
        uleb128 length;
        char chunkname[uleb128_value(length)];
    }
} GlobalHeader;

4.3 反编译脚本字节码(这段可能要重写,因为网上的这个工具好像反编译出来的效果不佳,可能需要先编译出编译器然后对照那个版本的opcode,然后把它更新进反编译器去)

反编译LuaJit脚本我们其实可以借助网上的反编译工具,现在打开CMD命令行:

cd /d <导出IPA的路径>
git clone https://github.com/DrNewbie/luajit-decompiler
cd luajit-decompiler
main.py --recursive ./<要反编译的路径> --dir_out ./<输出源文件的路径> --catch_asserts

经过反编译后结果如下图

4.4 重新编译脚本

要重新编译Lua的脚本到LuaJit我们得先去确认游戏的LuaJit的版本,最简单的方法就是回到IDA在字符串窗口中搜索LuaJit

得知他们用的是LuaJit 2.1.0-beta2之后(其实会看文件头也能知道他们的版本),那我们就打开CMD依次输入下面的命令

cd /d <导出IPA的路径>
git clone https://github.com/LuaJIT/LuaJIT
cd LuaJit
git tag(查看有没有LuaJit 2.1.0-beta2)
git checkout v2.1.0-beta2(如果这个游戏还是用的LuaJit 2.1.0-beta2的话)

打开Visual Studio->打开本地文件夹-><LuaJit路径>然后在菜单栏中工具->打开开发者命令提示(需要确认是x64,如果不是请自行打开C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat) 输入

cd src
msvcbuild gc64(如果目标编译器是打开了LUAJIT_ENGABLE_GC64的话)

等待编译脚本执行完成,然后可以打开CMD输入来编译脚本:

cd <LuaJit/src文件夹>
luajit -b<options(可不填)> <要编译的文件> <输出文件>

编译后的结构为下图

但检查了它的头文件之后你会发现它的FLAG对不上,我们编译出来的是0x02,而原本游戏编译出来的则是0x0a(我明明用的是gc64编译为啥?)

如果出现了这种情况说明你的GC64参数没被应用,那你需要手动打开并编辑msvcbuild.bat文件并将这行修改成:

@set LJCOMPILE=cl /nologo /c /O2 /W3 /D_CRT_SECURE_NO_DEPRECATE /D_LUAJIT_ENGABLE_GC64 /D_CRT_STDIO_INLINE=__declspec(dllexport)__inline

如果你用的是arch64处理器build的话,并且出现下面的错误

那你就需要把v2.1.10-beta3中的src/host/buildvm_arch.h这个文件复制粘贴到beta2版本的相同目录下,再运行msvcbuild.bat就能解决了!

image

beta3的比beta2的多了8kb,我也不想了解那多出来的8kb是什么=。=

今天先更新到这里吧,明天再更新了。

18 个赞

https://有空写/ 可还行

等更新。。

全过程 好快。。。

1 个赞

嗯嗯,看完了全过程受益匪浅。。。

哈哈哈,还是想先写逆向的过程。环境配置需要总结一下逆向的需求,所以我这里先写逆向过程。

哈哈哈哈,编辑一下。。没精力了。。。这么多工具请自行在论坛内或Google一个个找吧!

建议论坛可以做个目录导航

看完了,写得很详细很棒棒 :partying_face:

1 个赞

很棒:+1:t2: 期待后面的真机调试

1 个赞

牛逼,看完了,大有收获,好多疑惑的地方都解开了

1 个赞

厉害,逆向游戏方面,又增加了不少知识

很细很长

哈哈哈。。写到这里有点写不下去了。。哪天有精力了再往下写

1 个赞

希望读完对你有帮助

大佬这是刚入IOS逆向?之前做的windows逆向吗,强啊

是的。。感觉ios要用到的工具很多

有一款游戏 luac sign key也是隐藏的 有办法吗 叫挂机之王

可以按照这篇教程去找如果引擎是cocos2dx是可以找到的,不过luajit2.1.10-beta2不能GC64build不知道为什么,beta3可以,有时间我去看看能不能修(已经修好了)

这个写的是真的不错,学习到了

能帮到你就好 :grinning:

感激感激,正好这周末事不多,这游戏也一直想试试,周末动手玩玩,到时多多指教咯 :nose:

1 个赞