一、简介:
偶然在看雪看到一篇传奇手游封包解密的文章:
[原创]某传奇封包解密-软件逆向-看雪论坛-安全社区|安全招聘|bbs.pediy.com
但是内容说的太简略,所以我上手实战一番,此贴为实战记录。
文章中没有说是哪款传奇手游,根据图标我们去百度搜图锁定到一款《yscq》的手游。
二、抓包:
我们第一步就是进行抓包,确认是否和文章中说的是一款手游。
charles、Drony代理、r0capture、frida hook 关键发包位置
com.android.org.conscrypt.OpenSSLSocketImpl$SSLInputStream -> write libc.so -> write libssl.so -> SSL_write 以上方法我都尝试了。无法抓取socket数据。最后只能祭出通天神器 -> tcpdump
将文件放到 /data/local/tmp/tcpdump 并给予权限 chmod 777 tcpdump
tcpdump -p -vv -s 0 -w /sdcard/capture.pcap 抓包并记录到文件

这个头和文章里是不是很像,那么基本可以肯定就是这个游戏了。
解压APK 来到lib目录下,发现libMyGame.so文件最大,有20M。那么就它了。拖入IDA


确实可以搜索到文章中提到的函数,下面我们来具体分析一下,并打印调用栈。

根据调用栈我们追出大致调用流程
SendMsg -> SendNetMessage -> Encode6BitBuf -> send
其中hook SendMsg 就可以获取到明文发包内容。
function hookSend(){ // _ZN20LuaNetworkController7SendMsgEiiiiiPKcj var libcmodule = Process.getModuleByName("libMyGame.so"); var SendMsg = libcmodule.getExportByName("_ZN20LuaNetworkController7SendMsgEiiiiiPKcj"); console.log("SendMsg:",SendMsg); //void __fastcall LuaNetworkController::SendMsg(LuaNetworkController *this, int a2, int a3, int a4, int a5, int a6, const char *a7, size_t a8) Interceptor.attach(SendMsg, { onEnter: function (args) { this.arg0 = args[0]; this.arg1 = args[1]; this.arg2 = args[2]; this.arg3 = args[3]; this.arg4 = args[4]; this.arg5 = args[5]; this.arg6 = args[6]; this.arg7 = args[7]; console.log("SendMsg ",this.arg6); console.log("SendMsg ",this.arg7); console.log("SendMsg this.arg1: " + ptr(this.arg6).readCString()); LogPrint("go into libMyGame.so->SendMsg"); printNativeStack(this.context, "SendMsg"); }, onLeave(retval) { LogPrint("leave libMyGame.so->SendMsg " ); } }); } 这里有个小知识点,判断so加载后在加载hook代码,这样就不会出现找不到so文件的错误。
function testInit(){ var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext"); console.log(android_dlopen_ext); if(android_dlopen_ext != null){ Interceptor.attach(android_dlopen_ext,{ onEnter: function(args){ var soName = args[0].readCString(); console.log(soName); // libconnectionbase // if(soName.indexOf("MyGame.so") != -1){ if(soName.indexOf("connectionbase.so") != -1){ this.hook = true; } }, onLeave: function(retval){ if(this.hook) { hookSend(); }; } }); } } 
使用回城石SendMsg的传参!
第二部分:
Lua脚本加密解密:
首先我们先看看是cocos2d哪个版本,
在IDA中 shift+F12 调出Strings window窗口

Cocos2d-x对于lua脚本加密提供了一种轻量级解决方案, 加密算法是xxtea,具体可以github搜索xxtea开源算法
主要就是两个部分,sign和key,sign是加密标记,用于判断脚本是否加密,key是xxtea解密时候的秘钥
这里就是通过对比文件开头的字符串是否为所设置的sign判断是否加密,如果加密的话就解密,解密后才加载脚本

方法一、利用frida hook导出明文Lua脚本(最好在加载so完成第一时间hook哦~)
// 获取lua脚本文件 function hooklua(){ // luaL_loadbuffer var libMyGame = Process.getModuleByName("libMyGame.so"); console.log("libMyGame:",libMyGame); // var SendMsg = libcmodule.getExportByName("luaL_loadbuffer"); if (libMyGame){ //加载lua文件函数 Interceptor.attach(Module.findExportByName("libMyGame.so" , "luaL_loadbuffer"),{ onEnter:function (args){ // 文件保存路径 this.fileout = "/storage/emulated/0/frida/lua/" + Memory.readCString(args[3]).split("/").join("."); console.log("read file from: "+this.fileout); var tmp = Memory.readByteArray(args[1], args[2].toInt32()); var file = new File(this.fileout, "w"); file.write(tmp); file.flush(); file.close(); console.log("lual_loadbuffer (" +Memory.readCString (args[3] ) +" ," +Memory.readCString (args[1])+")"); }, onLeave:function (retval){ //console.log(retval) } }); } } 将文件夹复制到电脑,随便打开一个.lua文件
哇哦~哇哦~~!明文脚本。成功了。
方法二、找到sign和key,使用python编写加密解密函数
在解压文件中找到lua脚本文件(\assets\mod_launcher\stab\scripts),用记事本打开:

头部就是sign,在IDA中 ALT+T 字符串搜索sign(一般情况下key就在sign附近)。

好啦,现在sign和key都有了。让我们编写代码吧
import xxtea def luaDecode(orig_path,new_path): xxtea_sign="!ltcszip" xxtea_key="QvnHJr3kl6" orig_file = open(orig_path, "rb") encrypt_bytes = orig_file.read() orig_file.close() decrypt_bytes = xxtea.decrypt(encrypt_bytes[len(xxtea_sign):], xxtea_key) new_file = open(new_path, "wb") new_file.write(decrypt_bytes) new_file.close() def luaEncode(orig_path,new_path): xxtea_sign=b"!ltcszip" xxtea_key="QvnHJr3kl6" orig_file = open(orig_path, "rb") encrypt_bytes = orig_file.read() orig_file.close() decrypt_bytes = xxtea.encrypt(encrypt_bytes, xxtea_key) new_file = open(new_path, "wb") new_file.write(xxtea_sign + decrypt_bytes) new_file.close() if __name__ == '__main__': #解密 orig_path= r"D:\base\assets\mod_launcher\stab\scripts\main.lua" new_path= r"D:\test\main.lua" luaDecode(orig_path,new_path) #加密 orig_path= r"D:\lua\scripts.config.cfg_item.lua" new_path= r"D:\test\cfg_item.lua" luaEncode(orig_path,new_path) 好啦,加密解密都完成了。让我们来测试一下吧。
我们先来改个传送配置文件,看看是否有效果。例如:传送玛雅六层,需要5转和玛雅卷轴
[124] = { id=124, group=11, name="玛雅神殿", button=6, name1="玛雅六层", costitem=1, mapid="M019", positionx=83, positiony=84, condition="300005#400006", }, 我们把id为1的传送改成玛雅六层,并去除condition字段
[1] = { id=1, group=1, name="主城传送", button=1, name1="玛雅六层", costitem=1, mapid="M019", positionx=83, positiony=84, }, 现在需要把改过的.lua文件再加密回去,然后用MT放到指定目录。(如果加密后重新打包,进游戏时更新文件可能覆盖掉我们更改的文件)。
正常情况传送玛雅六层:

点我们更改的ID为1的传送:

其实这里是需要玛雅卷轴,不再需要转生5了。这个提示是因为我把本地提示的字符串改了。消耗物品这种应该是服务端有判断,所以改配置文件不太行。
好啦,基本的内容就是这些啦。感谢观看。