Cocos2dlua棋牌Lua解密

news2025/1/16 17:05:12

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


 介绍使用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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1810360.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

html+CSS+js部分基础运用20

根据下方页面效果如图1所示&#xff0c;编写程序&#xff0c;代码放入图片下方表格内 图1.效果图 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta http-equiv"X-UA-Compatible" conte…

WDF驱动开发-电源策略(三)

多组件设备的 KMDF 驱动程序只能将请求发送到处于活动状态的组件。 通常&#xff0c;驱动程序将 I/O 队列分配给组件或组件集。 首先考虑分配给单个组件的队列。 驱动程序在组件变为活动状态时启动队列&#xff0c;并在组件空闲时停止队列。 因此&#xff0c;当 KMDF 调用队列…

【Oracle生产运维】数据库服务器负载过高异常排查处理

说明 在Oracle数据库运维工作中&#xff0c;经常会遇到Oracle数据库服务器平均负载&#xff08;load average&#xff09;突然异常升高&#xff0c;如果放任不管&#xff0c;严重的情况下会出现数据库宕机、服务器重启等重大故障。因此&#xff0c;当发现数据库服务器平均负载…

2024年江苏三支一扶公告已出,招440人!

本次江苏省将招募440名高校毕业生&#xff0c;安排到乡镇&#xff08;街道&#xff09;从事支教、支农、支医、帮扶乡村振兴、水利、就业和社会保障服务工作&#xff08;以下简称“三支一扶”计划&#xff09;&#xff0c;服务期限为2年。 招募程序 招募工作按照个人报名、资格…

摩托罗拉手机在中国以外的市场复兴,在欧洲和美国大幅增长

摩托罗拉曾是全球手机行业的领导者&#xff0c;不过自从被诺基亚击败后&#xff0c;它就辗转被卖了又卖&#xff0c;曾经辉煌的品牌堕落了&#xff0c;让人颇为可惜&#xff0c;不过如今摩托罗拉手机似乎看到了复兴的希望&#xff0c;在中国以外的市场都取得了快速增长。 市调机…

GStreamer学习2.1----获取mp4中的图片

这里通过获取mp4中的图片例子来加深Gstreamer的理解&#xff0c;问问AI实现这样功能的命令&#xff0c; 得到 gst-launch-1.0 filesrc locationtest.mp4 ! qtdemux ! queue ! h264parse ! avdec_h264 ! videoconvert ! jpegenc ! multifilesink locationoutput_image_%03d.jp…

linux centos consul1.15.2一键安装部署

consul原理、作用、安装相关内容 一、理论部分二、安装下载版本地址三、安装consul服务 一、理论部分 1、consul的原理 Consul的原理及作用可以归纳为以下几点&#xff1a; ①、基于Gossip协议的通信&#xff1a;Consul使用了基于Gossip协议的Serf实现来进行通信。 Gossip协议…

【思考】Vue2响应丢失、$set

【思考】Vue2响应丢失、$set vue2响应丢失情况复现原因解决总结 vue2响应丢失情况复现 场景&#xff1a;直接通过数组下标去修改数组造成响应丢失 <template><div><p v-for"(item, index) in list" :key"index">{{item}}</p><…

《Brave New Words 》4.4 ​增加父母与孩子之间的连接点

Part IV Better Together 第四部分 携手共进 Increasing Points of Connection Between Parents and Their Kids 增加父母与孩子之间的连接点 From the moment I first used GPT-4 to the day our team concluded its hack-AI-thon, I found myself awed by its capabilities. …

设计模式-装饰器模式(结构型)

装饰器模式 装饰器模式是一种结构模式&#xff0c;通过装饰器模式可以在不改变原有类结构的情况下向一个新对象添加新功能&#xff0c;是现有类的包装。 图解 角色 抽象组件&#xff1a;定义组件的抽象方法具体组件&#xff1a;实现组件的抽象方法抽象装饰器&#xff1a;实现…

effective-C++ 条款6

&#x1f536;条款6——不能被拷贝的类  &#x1f536;delete 关键字能禁掉自动生成的函数  &#x1f531;让他的派生类不能被继承  &#x1f531;另一种实现派生类不能被拷贝的方法 条款6——不能被拷贝的类 1. delete 关键字能禁掉自动生成的函数 class Uncopy { publi…

【解决问题】QApplication: No such file or directory,C++ 使用Qt或项目未正确加载Cmake报错

运行环境&#xff1a; Clion编译&#xff0c;构建C工程项目报错QApplication: No such file or directory 问题描述 QApplication: No such file or directory 引用的#include <QApplication>飘红 解决方案 1、Qt没有安装正确&#xff0c;请使用对应版本的Qt。或编译…

Nodejs 第七十六章(MQ进阶)

MQ介绍和基本使用在上一章介绍过了&#xff0c;不再重复 消息&#xff1a;在RabbitMQ中&#xff0c;消息是传递的基本单元。它由消息体和可选的属性组成 生产者Producer&#xff1a;生产者是消息的发送方&#xff0c;它将消息发送到RabbitMQ的交换器&#xff08;Exchange&…

php 混合xml js,html 代码报错 ,结束标签关闭, short_open_tag 的作用,php关闭文件结束判断

结束标签关闭, short_open_tag 的作用&#xff0c;php关闭文件结束判断 有时候我们我们会将php&#xff0c;xml&#xff0c;js&#xff0c;html 混合编写 php文件只要开始标签而不要结尾标签? 混合代码看代码 直接运行 yntax error, unexpected version (T_STRING) in php…

【差分数组】1674. 使数组互补的最少操作次数

本文涉及知识点 差分数组 LeetCode1674. 使数组互补的最少操作次数 给你一个长度为 偶数 n 的整数数组 nums 和一个整数 limit 。每一次操作&#xff0c;你可以将 nums 中的任何整数替换为 1 到 limit 之间的另一个整数。 如果对于所有下标 i&#xff08;下标从 0 开始&…

reverse入门刷题(6.9)

总结&#xff1a; 拿到附件&#xff0c;先运行看看有没有信息&#xff0c;再查壳&#xff0c;再IDA运行 1.Easy_vb 收获&#xff1a; 使用搜索&#xff1a;在String的时候用的是ctrlf 在IDA_view的时候使用搜索是Aitt 打开IDA&#xff0c;Aitt搜索MCTF&#xff08;关键字即…

2024年6月最新开源电视影视TVAPP原生源码和后台管理平台源码及完整教程

本套源码为本人维护更新完善半年左右的还在使用开发的源码&#xff0c;与市面上倒卖的残次品不一样&#xff0c;没有可比性&#xff0c;向下兼容安卓4.0&#xff0c;向上兼容安卓13以上TV电视系统&#xff0c; 完全无闪退&#xff0c;弹窗报错&#xff0c;卡死、异常死循环残次…

加解密算法及国密算法应用

常见的加解密算法可以分为可逆和不可逆两种 不可逆算法 哈希算法&#xff0c;MD5&#xff0c;hs-256&#xff0c;SM3 一般系统中使用密码加密和数据防篡改校验字段就是不可逆算法 hs-256应用&#xff1a;JWT header头部payload荷载signature签名(防止篡改) 前两部分采用base…

数组双指针经典习题

合并两个有序数组 class Solution {public void merge(int[] nums1, int m, int[] nums2, int n) {int p1m-1,p2n-1;int p3nums1.length-1;while(p1>0&&p2>0){//放完一个数组if(nums1[p1]>nums2[p2]){nums1[p3--]nums1[p1];p1--;}else{nums1[p3--]nums2[p2];p…

微服务Day7学习-数据聚合、同步、补全

文章目录 数据聚合聚合分类 自动补全DSL实现Bucket聚合DSL实现Metrics聚合RestAPI实现聚合多条件聚合对接前端接口拼音分词器自定义分词器自动补全查询实现酒店搜索框自动补全 数据同步数据同步思路分析利用mq实现mysql与elasticsearch数据同步 集群介绍搭建ES集群 数据聚合 聚…