不思议迷宫:逆向后的放置play

  1. 前言
  2. 前置准备
  3. 目标分析
  4. 逆向加密逻辑
  5. 定位sign与key
  6. 解密luac
  7. 反编译luajit的bytecode
  8. 开启上帝模式
  • 前言

    看了fatezero的《阴阳师:一个非酋的逆向旅程》后,受益匪浅。特别是关于opcode映射关系一节,处理的很精妙。

对手头上的游戏不思议迷宫,技痒的不行。于是上周周六花了整个下午的时间进行了研究。注意本文中针对安卓版本。

  • 前置准备

    如果读者也想跟着步骤进行操作,需要准备这些工具:ApkIDE少月版、IDA、python、010editor

  • 目标分析

     APKIDE载入之后,首先看一下libarmeabi目录下,发现了libcocos2dlua.so

     

  从字面意思来看,应该是使用cocos2d引擎,并且使用lua脚本,再看assetssrc

   

  发现大量的luac脚本,进一步确认了我们的想法。010editor打开Main.luac,从头来看,并非luac文件的头

  

  正常的luaca的头应该是1B 4C 75 61开头,如下图

  

  所以不思议迷宫必然是对luac进行了加密。

  • 逆向加密逻辑

  通过IDA打开libcocos2dlua.so,一般情况下加密会出现cocos2dx_lua_loader->luaL_loadbuffer的某个过程中

  源码如下:

   

  而在IDA中,luaL_loadbuffer之前出现了srcDecrypt函数,这可是源码中没有出现了。

  

  数据的流向是:从文件读入->v51->v25->luaL_loadbuffer,再分析srcDecrypt函数

  

  当一个文件的头为11 12 13 的时候,就用charMapList进行替换,而charMapList,通过引用查找

  又是从buildEncrypyMap中初始化的,显然这是一组“静态”的置换表,完全可逆而且没有任何难度。

  

  但是回过头来看apk中的luac文件,没有一个的文件头是11 12 13,文件头全部是applicationWillEnterForeground

  并没有给我们带来任何帮助,只能继续分析luaLoadBuffer,看到了第二个加密的地方xxtea_decrypt

  

  google,baidu之后,找到非常类似的一段源码

   

  通过sign对文件进行标记,符合条件用key进行解密,梳理一下luac的整体解密过程

  

  由于没有使用srcDecrypt的流程,所以实际上只有xxtea_decrypt,只要找到sign和key,问题就解决了。

  • 定位sign与key 

   通过分析,可以确定加密的最终文件格式。文件头都有固定长度的sign

xxtea_decrypt(buf+decode->m_xxteaSignLen, (xxtea_long)size -(xxtea_long)decode->m_xxteaSignLen, (unsigned char*)decode->m_xxteaKey, (xxtea_long)decode->m_xxteaKeyLen, &len); 

  再次打开另外一个luac文件,二者相同的文件头如图,所以sign为applicationWillEnterForeground

  

  在IDA中,我们也找到了这个字符串

  

  查找引用之后,这个字符串在initLuaStack中被调用了。

  

  再通过资料搜索,发现一般使用xxtea算法的,都会使用setXXTEAKeyAndSign来设置sign和key,图中v3就是setXXTEAKeyAndSign函数

stack->setXXTEAKeyAndSign("123", strlen("123"), "cloud", strlen("cloud"));  

  那么自然key就是:applicationDidEnterBackground

  • 解密luac

  首先pip install xxtea-py,安装python的xxtea的库

  编写脚本如下:

import xxtea
import os
sign = 'applicationWillEnterForeground'
key = 'applicationDidEnterBackground'
def decode(filename):
    luacdata = open(filename,'rb').read()
    decrypt_data = xxtea.decrypt(luacdata[len(sign):],key[:16])
    open(filename.replace('.luac','.luacx'),'wb').write(decrypt_data)

  解密后如下,出现的文件头为1B 4C 4A 01,也并非luac标准头,难道还有名堂?

  

  经过一番资料查询之后,这是luajit编译的bytecode,并非标准lua。其头为1B 4C 4A


  


  • 反编译luajit的bytecode

  在github上找到用于反编译luajit的项目:https://github.com/NightNord/ljd

   由于该库只能在python3上跑起来,而我懒得改脚本,于是直接通过命令执行方式,在kali中(同时存在python2、python3)运行了如下脚本

#coding=utf-8
import os
sign = 'applicationWillEnterForeground'
key = 'applicationDidEnterBackground'
path = os.path.join(os.getcwd(),'src') 
def decode(filename):
    luacdata = open(filename,'rb').read()
    decrypt_data = xxtea.decrypt(luacdata[len(sign):],key[:16])
    open(filename.replace('.luac','.luacx'),'wb').write(decrypt_data)

def decomplie(filename):
    os.system('python3 ./ljdm/main.py '+filename+' > '+filename.replace('.luacx','.luacxs'))

    
for path,d,filelist in os.walk(path):  
    for filename in filelist:
        filename = os.path.join(path, filename)
        if filename.endswith('.luac') and not os.path.isfile(filename.replace('.luac','.luac')):
            print( filename)
            decode(filename)
        if filename.endswith('.luacx') and (not os.path.isfile(filename.replace('.luacx','.luacxs')) or os.path.getsize(filename.replace('.luacx','.luacxs'))==0):
            print(filename)
            decomplie(filename)

  src目录对应生成的luacxs就是最终反编译好的lua源代码了。

  • 开启上帝模式

  通过大量的分析之后,我发现程序员在代码中,没有去掉测试用的上帝模式,只是简单的隐藏

  我在UIAccountBind.luacxs:380行中加入打开上帝模式的一行代码openGdUI().

  这样当我点击复制账号id的时候就会弹出上帝模式窗口

    local function onCopyClick(sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            copyToClipBoard(rid, getLocStr("text_copied"))
        end
        openGdUI()
        return 
    end

  以及GDM.luacxs:24中的check_mode,让其始终返回为true,保证上帝模式功能可正常开启

function check_mode()
    return true 
end

  然后通过luajit编译,再进行xxtea加密,打包成APK放入手机。

  最终效果图为:全自动放置play,程序员已经实现了自动加天赋,自动捡东西,自动打怪,自动进入下一层等等

  

原文地址:https://www.cnblogs.com/howmp/p/6528355.html