一、简介:
偶然在看雪看到一篇传奇手游封包解密的文章:
[原创]某传奇封包解密-软件逆向-看雪论坛-安全社区|安全招聘|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了。这个提示是因为我把本地提示的字符串改了。消耗物品这种应该是服务端有判断,所以改配置文件不太行。
好啦,基本的内容就是这些啦。感谢观看。