Cocos2dlua棋牌Lua解密

点击上方蓝字[协议分析与还原]关注我们


 介绍使用libcocos2dlua.so库的游戏的解密分析方法。

Cocos2dlua是一款流行的游戏引擎,常用于开发棋牌游戏。为了保护游戏代码,Cocos2dlua通常会对游戏脚本lua文件进行加密,生成Luac文件,内容一般长这样,前面会有几个固定字符串,对比几个文件就能确定它的内容:

6dd348112c45032aecec92e7437391ba.png

上面这个luac文件的固定字符串就是tianlianmjXXTEA。

对cocos2dlua游戏进行分析,获取里面的算法或者一些参数,首先需要做的是对这个文件进行解密。

01

找密钥

使用Android Killer打开APK,看下文件的结构,很容易lib目录下看到armeabi目录,里面有几个so文件,其中,libcocos2dlua.so就是luac文件解密的关键所在,我们需要从这个so里面搞定加密密钥和算法。

71107783d5fd2f0b51dc6d26b7f6b436.png

如果这个APK使用的密钥是在cocos2d里面常规配置的,则很容易在so文件里面找到,使用IDA打开so,直接搜前面看到的luac文件前面的固定字符串,它是文件的签名,我们把它叫sign,这个例子里面是:tianlianmjXXTEA。前后看看,一般如果有一串比较长的无规律字符串,我们需要找的密钥key就是它了。但是,有的时候这个密钥key找不到,因为它不是常规配置的,这个时候密钥key的取法需要知道加密算法,我们后面再说,这里先说加密算法。

虽然so分析很耗时且繁琐,这里就直接上结论,cocos2d对lua文件的加密使用的是xxtea算法,在ida对so自动分析完之后,直接搜xxtea_decrypt即可定位函数位置,看下算法,不用担心,这就是你想要的解密算法了,你不用去抠它,它的核心是常规的xxtea,大部分编程语言都有很好的实现供你使用,看函数的上下文,读取luac文件,对sign进行对比,对文件去sign之后的内容使用密钥key进行xxtea解密,对解密结果进行处理。这个解密,不仅仅解密luac文件,还解密各种其它文件。

一个简单的python解密函数实现如下:

dec_data = xxtea.decrypt(data=data[len(sign):], key=key, padding=False)

接下来,需要的是解密的密钥key,当在so里面不容易找到的时候,我们需要使用hook来找了,我们可以使用frida来实现hook,如果不会,上frida官网去学习学习,基础的很简单,我们不需要学习复杂的花哨知识,够用就行,百分之八十的人只需要掌握基础的百分之八十就可以了。

我们需要hook的是libcocos2dlua.so这个文件里面的xxtea_decrypt,我们在前面已经找到了它,知道了它在ida里面的偏移地址,这里分析的样本应用的地址是0x4E1ECA,但是我们还要注意一点Thumb指令集模式和Arm指令集模式的区别,如果是Thumb指令集,inline hook的偏移地址需要进行+1,很巧,样本应用就是Thumb指令集,所以我们的偏移地址是0x4E1ECB,hook跑起来,刷刷的刷屏,这就是我们需要的东西:

343fe929b58fe2e1f510ded10359afb2.png

上面的arg2里面就是我们我们需要的解密密钥key,arg3是key的长度,在打印出的内存里面,紧跟着key的内容是sign,很容易识别它。

至此,解密算法,解密的sign,key都找到了,接下来就是写程序实现算法批量解密luac文件了。

02


文件解密

现在回来继续关注我们要解密的luac文件,从apk里面解出的目录下,有很多luac,我们需要实现的是自动化的解密,而不是将luac文件一个个找出来解密输出。这里当然是使用python来实现解密。

首先需要实现的是单个luac文件的解密,读取文件,解密,输出解密结果。

def read_jsc_file(path):
    f = open(path, "rb")
    data = f.read()
    f.close()
    return data
def decrypt(filePath, key,sign):
    data = read_jsc_file(path=filePath)
    if data.startswith(sign):
        if(len(data)>len(sign)):
            dec_data = xxtea.decrypt(data=data[len(sign):], key=key, padding=False)
        else:
            return b''
    else:
        dec_data = xxtea.decrypt(data=data, key=key, padding=False)
    if dec_data[:2] == b"PK":
        fio = BytesIO(dec_data)
        fzip = zipfile.ZipFile(file=fio)
        dec_data = fzip.read(fzip.namelist()[0])
    elif dec_data[:2] == b"\x1f\x8b":
        dec_data = bytes(zlib.decompress(dec_data, 16 + zlib.MAX_WBITS))#.decode("utf-8")
    else:
        l=dec_data[-4:]
        l=int.from_bytes(l, byteorder='little', signed=False)
        dec_data = bytes(dec_data[0:l])
    return dec_data

接下来,外面再套一层遍历文件夹里的合格文件。

def save_file(fileDir, outData):
    rootPath = os.path.split(fileDir)[0]
    try:
        os.makedirs(rootPath)
    except OSError:
        if not os.path.exists(rootPath):
            raise Exception("Error: create directory %s failed." % rootPath)
    if fileDir.endswith("c"):
        file = fileDir[:-1]
    with open(file, "wb") as fd:
        fd.write(outData)
    fd.close()
def dir_decrypt(srcDir, xxtea_key,signkey):
    if not os.path.exists(srcDir):
        ColorPrinter.print_white_text("Error:FileNotFound")
        exit(1)
    rootDir = os.path.split(srcDir)[0]
    outDir = rootDir
    if len(outDir)>0 and outDir[-2:-1] != "\\":
        outDir += "\\"


    outDir += "out\\"
    traveDir.deep_iterate_dir(srcDir)
    files_list = traveDir.getfileslist()
    for file_path in files_list:
        ColorPrinter.print_green_text("Decrypting flie:{0}".format(file_path))
        decData = decrypt(filePath=file_path, key=xxtea_key,sign=signkey)
        outFile = outDir + file_path[len(rootDir + os.path.split(srcDir)[1]) + 1:]
        save_file(fileDir=outFile, outData=decData)
        print("        Save flie:{0}".format(outFile))

最后,需要写整个功能的入口。

def main():
    instruct = sys.argv[1]
    xxtea_key = sys.argv[2]
    bsignkey=sys.argv[3].encode()
    srcDir = sys.argv[4]
    if len(xxtea_key)<16:
        taillen=16-len(xxtea_key)
        bxxtea_key=xxtea_key.encode().ljust(16,b'\0')
    else:
        bxxtea_key = xxtea_key.encode()[0:16]
    if instruct[1:2] == "d":
        dir_decrypt(srcDir=srcDir, xxtea_key=bxxtea_key,signkey=bsignkey)
if __name__ == "__main__":
     main()

拼拼凑凑,就将luac文件的解密完成了,不难。

03


结束

整个luac解密过程的重点是获取解密key,而sign可以用来帮助我们定位它,如果大家有什么需要,可以一起交流交流,high起来。

别忘点“在看”、“赞”和“分享”

新的规则,及时收推文要先给公号星标

别忘了星标一下,不然就错过了

926b0b32452de4ea2b9f457d417b467a.jpeg

长按进行关注,时刻进行交流。

相关推荐

  1. cocos2d-x lua ProgressTimer

    2024-06-12 06:30:02       41 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-12 06:30:02       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-12 06:30:02       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-12 06:30:02       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-12 06:30:02       18 阅读

热门阅读

  1. 海外盲盒小程序背后的技术支撑与实现

    2024-06-12 06:30:02       4 阅读
  2. CAPL如何在底层模拟TCP Server端建立TCP连接

    2024-06-12 06:30:02       11 阅读
  3. 低代码开发应用:国企数字化转型的思考与探索

    2024-06-12 06:30:02       7 阅读
  4. 项目中常量的定义方式

    2024-06-12 06:30:02       5 阅读