吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4218|回复: 7
收起左侧

[Android 原创] 某cocos2dx MOBA游戏分析

  [复制链接]
a1046830 发表于 2021-3-31 16:56
1、小地图的战争迷雾一般对于MOBA游戏透视来说,用GG修改器就足以很简单的实现了:开局敌人全部在小地图上看不到,那直接搜0就行了,然后对着对面一个特定的英雄,简称H。当H小头像出现在小地图上的时候搜1,没有的时候搜0。这样很快就能找到H在内存中的数据,将那个值改成1冻结掉就可以实现H的小地图透视了。如果说想实现对面全部英雄透视的话。那就在H的那个“1”值的内存中往上找特征码,然后搜索特征码+偏移,找到的一起改成1就行了。这样的结果一般是对面塔、小兵、水晶、英雄一起暴露在小地图的视野里面。
实现的GG修改器脚本可以参考以下代码
[Lua] 纯文本查看 复制代码
-----------------------------------------------------------------------------------------

function split(szFullString, szSeparator) 
local nFindStartIndex = 1 
local nSplitIndex = 1 
local nSplitArray = {} 
while true do 
local nFindLastIndex = string.find
(szFullString, szSeparator, nFindStartIndex) 
if not nFindLastIndex then 
nSplitArray[nSplitIndex] = 
string.sub(szFullString, nFindStartIndex, string.len
(szFullString)) 
break end 
nSplitArray[nSplitIndex] = string.sub
(szFullString, nFindStartIndex, nFindLastIndex - 1) 
nFindStartIndex = nFindLastIndex + string.len
(szSeparator) 
nSplitIndex = nSplitIndex + 1 end return 
nSplitArray end 
function xgxc(szpy, qmxg) 
for x = 1, #(qmxg) do 
xgpy = szpy + qmxg[x]["offset"] 
xglx = qmxg[x]["type"] 
xgsz = qmxg[x]["value"] 
xgdj = qmxg[x]["freeze"] 
if xgdj == nil or xgdj == "" then 
gg.setValues({[1] 
= {address = xgpy, flags = xglx, value = xgsz}}) 
else 
gg.addListItems({[1] 
= {address = xgpy, flags = xglx, 
freeze = xgdj, value = xgsz}}) end 
xgsl = xgsl + 1 xgjg = true end end 
function xqmnb(qmnb) 
gg.clearResults() 
gg.setRanges(qmnb[1]["memory"]) 
gg.searchNumber(qmnb[3]["value"], qmnb[3]["type"]) 
if gg.getResultCount() == 0 then 
gg.toast(qmnb[2]["name"] .. "开启失败") 
else 
gg.refineNumber(qmnb[3]["value"], qmnb[3]["type"])
gg.refineNumber(qmnb[3]["value"], qmnb[3]["type"]) 
gg.refineNumber(qmnb[3]["value"], qmnb[3]["type"]) 
if gg.getResultCount() == 0 then 
gg.toast(qmnb[2]["name"] .. "开启失败") 
else 
        sl = gg.getResults(999999) 
sz = gg.getResultCount() 
        xgsl = 0 if sz > 999999 then 
sz = 999999 end for i = 1, sz do 
pdsz = true for v = 4, #(qmnb) do if 
pdsz == true then 
pysz = {} pysz[1] 
= {} pysz[1].address 
= sl[i].address + qmnb[v]["offset"] pysz[1].flags 
= qmnb[v]["type"] 
szpy = gg.getValues(pysz) 
pdpd = qmnb[v]["lv"] .. ";" .. szpy[1].value szpd 
= split(pdpd, ";") tzszpd 
= szpd[1] pyszpd = szpd[2] 
if tzszpd == pyszpd then 
pdjg = true pdsz = true else 
pdjg = false pdsz = false end end end if 
pdjg == true then szpy 
= sl[i].address xgxc(szpy, qmxg) end end 
if xgjg == true then 
gg.toast(qmnb[2]["name"] .. "开启成功,共修改" .. xgsl .. "条数据") 
else 
gg.toast(qmnb[2]["name"] .. "开启失败") 
end 
end 
end 
end
-------配置↑↑↑勿修改-------
-------支持冻结------
-----------------------------------------------------------------------------------------
function Main()
  SN = gg.choice({
  "扩大视野开启",
  "扩大视野关闭",
  "小视野开启",
  "小视野关闭",   
   "锁头像透视开启",
  "锁头像透视关闭",
  "高视角(小型)",
  "高视角(小型关闭)",
  "高视角(中型)",
  "高视角(中型关闭)",
  "高视角(大型)",
  "高视角(大型关闭)",
   "高视角(巨型)",
  "高视角(巨型关闭)",  
"退出脚本"
}, nil, "")
  if SN == 1 then a() end
  if SN == 2 then b() end
  if SN == 3 then c() end
  if SN == 4 then d() end
  if SN == 5 then e() end
  if SN == 6 then f() end
  
  if SN == 7 then g() end
  if SN == 8 then h() end
    if SN == 9 then i() end
  if SN == 10 then j() end
  if SN == 11 then k() end
  if SN == 12 then l() end
  if SN == 13 then m() end
  if SN == 14 then n() end


  if SN == 15 then os.exit() end XGCK = -1 end
     

function a()
qmnb = {
{["memory"] = gg.REGION_C_ALLOC},
{["name"] ="视野扩大开启"},
{["value"] =, ["type"] = 4},
{["lv"] = , ["offset"] = -4, ["type"] = 4},
{["lv"] = , ["offset"] = 4, ["type"] = 4},
{["lv"] = , ["offset"] = -8, ["type"] = 4},
 }
qmxg = {
{["value"] =1, ["offset"] = 20, ["type"] = 4},
}
xqmnb(qmnb)
end


function b()
qmnb = {
{["memory"] = gg.REGION_C_ALLOC},
{["name"] ="视野扩大关闭"},
{["value"] =, ["type"] = 4},
{["lv"] = , ["offset"] = -4, ["type"] = 4},
{["lv"] = , ["offset"] = 4, ["type"] = 4},
{["lv"] = , ["offset"] = -8, ["type"] = 4},
 }
qmxg = {
{["value"] =, ["offset"] = 20, ["type"] = 4},
}
xqmnb(qmnb)
end

function c()
qmnb = {
{["memory"] = gg.REGION_C_ALLOC},
{["name"] ="小视野开启"},
{["value"] =, ["type"] = 4},
{["lv"] = , ["offset"] = -4, ["type"] = 4},
{["lv"] = , ["offset"] = -12, ["type"] = 4},
{["lv"] = , ["offset"] = -24, ["type"] = 4},
 }
qmxg = {
{["value"] =1, ["offset"] = 12, ["type"] = 4},
}
xqmnb(qmnb)
end

function d()
qmnb = {
{["memory"] = gg.REGION_C_ALLOC},
{["name"] ="小视野关闭"},
{["value"] =, ["type"] = 4},
{["lv"] = , ["offset"] = -4, ["type"] = 4},
{["lv"] = , ["offset"] = -12, ["type"] = 4},
{["lv"] = , ["offset"] = -24, ["type"] = 4},
 }
qmxg = {
{["value"] =0, ["offset"] = 12, ["type"] = 4},
}
xqmnb(qmnb)
end


function e()
qmnb = {
{["memory"] = gg.REGION_C_ALLOC},
{["name"] ="锁头像透视开启"},
{["value"] =, ["type"] = 4},
{["lv"] = , ["offset"] = -12, ["type"] = 4},
{["lv"] = , ["offset"] = -24, ["type"] = 4},
{["lv"] = , ["offset"] = -32, ["type"] = 4},
 }
qmxg = {
{["value"] =1, ["offset"] = -64, ["type"] = 4,["freeze"] = true},
}
xqmnb(qmnb)
end

function f()
qmnb = {
{["memory"] = gg.REGION_C_ALLOC},
{["name"] ="锁头像透视关闭"},
{["value"] =, ["type"] = 4},
{["lv"] = , ["offset"] = -12, ["type"] = 4},
{["lv"] = , ["offset"] = -24, ["type"] = 4},
{["lv"] = , ["offset"] = -32, ["type"] = 4},
 }
qmxg = {
{["value"] =1, ["offset"] = -64, ["type"] = 4,["freeze"] = false},
}
xqmnb(qmnb)
end



function g()
	 gg.clearResults()
	 gg.setRanges(37)
	 gg.searchNumber("", gg.TYPE_FLOAT, false, gg.SIGN_EQUAL, 0, -1)
	 gg.getResults(100)
	 gg.editAll("", gg.TYPE_FLOAT)
	 gg.toast("小型高视角开启成功")
	 gg.clearResults()
end

function h()
gg.clearResults()
	 gg.setRanges(37)
	 gg.searchNumber("", gg.TYPE_FLOAT, false, gg.SIGN_EQUAL, 0, -1)
	 gg.getResults(100)
	 gg.editAll("", gg.TYPE_FLOAT)
	 gg.toast("小型高视角关闭成功")
	 gg.clearResults()
end

function i()
gg.clearResults()
	 gg.setRanges(37)
	 gg.searchNumber("", gg.TYPE_FLOAT, false, gg.SIGN_EQUAL, 0, -1)
	 gg.getResults(100)
	 gg.editAll("", gg.TYPE_FLOAT)
	 gg.toast("中型高视角开启成功")
	 gg.clearResults()
end

function j()
gg.clearResults()
	 gg.setRanges(37)
	 gg.searchNumber("", gg.TYPE_FLOAT, false, gg.SIGN_EQUAL, 0, -1)
	 gg.getResults(100)
	 gg.editAll("", gg.TYPE_FLOAT)
	 gg.toast("中型高视角关闭成功")
	 gg.clearResults()
end


function k()
gg.clearResults()
	 gg.setRanges(37)
	 gg.searchNumber("", gg.TYPE_FLOAT, false, gg.SIGN_EQUAL, 0, -1)
	 gg.getResults(100)
	 gg.editAll("", gg.TYPE_FLOAT)
	 gg.toast("大型高视角开启成功")
	 gg.clearResults()
end


function l()
gg.clearResults()
	 gg.setRanges(37)
	 gg.searchNumber("", gg.TYPE_FLOAT, false, gg.SIGN_EQUAL, 0, -1)
	 gg.getResults(100)
	 gg.editAll("", gg.TYPE_FLOAT)
	 gg.toast("大型高视角关闭成功")
	 gg.clearResults()
end


function m()
gg.clearResults()
	 gg.setRanges(37)
	 gg.searchNumber("", gg.TYPE_FLOAT, false, gg.SIGN_EQUAL, 0, -1)
	 gg.getResults(100)
	 gg.editAll("", gg.TYPE_FLOAT)
	 gg.toast("巨型高视角开启成功")
	 gg.clearResults()
end


function n()
gg.clearResults()
	 gg.setRanges(37)
	 gg.searchNumber("", gg.TYPE_FLOAT, false, gg.SIGN_EQUAL, 0, -1)
	 gg.getResults(100)
	 gg.editAll("", gg.TYPE_FLOAT)
	 gg.toast("巨型高视角关闭成功")
	 gg.clearResults()
end







while true do

  if gg.isVisible(true) then
    XGCK = 1
    gg.setVisible(false)
  end
  gg.clearResults()
  if XGCK == 1 then
    Main()
  end
end



如果说不喜欢用GG修改器,而且喜欢frIDA的话。可以参考以下:
cocos2dx中,MOBA的地图、战争迷雾、英雄的头像在小地图上不在一个layer里面,关于迷雾的实现,有一大段乱七八糟的东西,具体实现关心的话可以搜索cocos2dx 战争迷雾。这里我们不关心战争迷雾的实现方式。
在我分析的那个游戏里面,关于英雄在小地图上实现头像显示的流程如下:首先它会将一个房间里面的英雄分成两个阵营:0和1;然后在0和1阵营里面分别给五个英雄做标号,通俗来说就是2个数组。在游戏加载时,系统会做一些初始化的东西,关于战争迷雾的R半径初始化和是否隔墙显示战争魔物这些,最重要的是它会让1阵营的英雄的小头像不在0阵营英雄的小地图上。
image.png
然后关于在游戏中小头像在地图上的显示(也就是战争迷雾的显示与遮盖)实际上是在一个帧同步的进程里面。而且它的计算是放在本地进行的。为什么它会放到本地主要是因为手机网速不稳定,性能差异大,而且moba游戏对延时要求高。
而且一般cocos2dx游戏的主要游戏逻辑是放在这个游戏安装包里最大的那个so里面进行的,为什么是最大的?
主要是一般cocos2dx游戏都内嵌了lua引擎,所以最大的so最有可能含有lua引擎。
如何查找战争迷雾的显示与遮盖:
对于一般的cocos2dx游戏来说,你需要查找什么函数你就在cocos2dx官方文档上找这种函数的实现名字,找到它的名字直接在so里面搜就行了。
比如说战争迷雾,先搜索process,找找游戏逻辑主进程。
process.png
最笨的办法就是全部hook,看那个函数会不停地被调用。定位到bloodMoveString2::process(ulong long)这个函数
查找这个函数出现warfog的名字,然后跳过去看
迷雾1.png
迷雾2.png
这个很像,isWarfogVisble。F5反编译
QQ图片20210331164725.png
bool函数,而且看代码也很像。应该是了。然后frida hook一下,成功。
[JavaScript] 纯文本查看 复制代码
function hook_WarforgOpen(){// var warfogoepn=Module.findExportByName("libcos.so","_ZN4CMap14isWarfogVisbleEff");
//console.log("the addr of this func is :",warfogoepn);
Interceptor.attach(flagaddr,{
onEnter:function(args){
//console.log("enter this merhod successful",args[1]);
},onLeave:function(retval){
//console.log(retval);
retval.replace(1);
// console.log("the retval changged is :",retval);
// retval.replace(201); //修改flag 同样可以生效
}
});
}


2、lua脚本dump
cocos2dx游戏lua脚本dump时机点就在loadbuffer这个函数里面。一般找到这个函数就是了luaex_loadbuffer(lua_State *,char const*,uint,char const*) 或者相似的。
找到直接frida就能dump下来。我分析的这个游戏没有加密所有dump下来的全是明文,如果加密的话,可以顺着这个函数往上找,应该可以找到解密函数。
[Asm] 纯文本查看 复制代码
function hook_LuaBufferload(){
    var loadbuffer=Module.findExportByName("libcos.so","_ZN2tq16luaex_loadbufferEP9lua_StatePKcjS3_");
    console.log("the addr of this func is :",loadbuffer);
    Interceptor.attach(loadbuffer,{
        onEnter:function(args){
                 console.log(ptr(args[3]).readCString()); 
                //参数3是lua放置的位置和名字
                var tmp =ptr(args[3]).readCString();
                var tmp1=tmp.split("/");
                var siz=tmp1.length;
                //console.log(tmp1[siz-1]);
                //console.log("大小是:",args[2].toInt32());
                write_lua(tmp1[siz-1],ptr(args[1]).readCString());
        },onLeave:function(retval){
        }
    });


}

function write_lua(name , content){
        //frida 的api来写文件
        var file = new File("/sdcard/lua/"+name, "w");
        file.write(content);
        file.flush();
        file.close();
  }



dump下来的lua脚本存放在sdcard/lua文件夹下面。直接adb pull拉出来就好了

免费评分

参与人数 7吾爱币 +6 热心值 +7 收起 理由
卖血上网 + 1 + 1 谢谢@Thanks!
goushi + 1 谢谢@Thanks!
TickDown + 1 + 1 谢谢@Thanks!
石碎大胸口 + 1 + 1 谢谢@Thanks!
imeteor + 1 + 1 谢谢@Thanks!
鱼丸车仔面 + 1 + 1 热心回复!
HuZH + 1 + 1 我很赞同!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

一路无忧 发表于 2021-3-31 21:19
厉害了 值得学习
卖血上网 发表于 2021-3-31 22:38
大佬有空闲研究一下 全球行动这个游戏的迷雾么,手机版红警 毛子游戏去年tx代{过}{滤}理
oostudy 发表于 2021-4-1 09:51
阳光好青年 发表于 2021-4-1 15:47
厉害!想知道 找0 找1 的思想是什么?是框架有相关参数设定吗?
 楼主| a1046830 发表于 2021-4-1 16:03
阳光好青年 发表于 2021-4-1 15:47
厉害!想知道 找0 找1 的思想是什么?是框架有相关参数设定吗?

0说明敌方小头像在地图上不显示,1就是相反
 楼主| a1046830 发表于 2021-4-1 16:59
卖血上网 发表于 2021-3-31 22:38
大佬有空闲研究一下 全球行动这个游戏的迷雾么,手机版红警 毛子游戏去年tx代{过}{滤}理

战争迷雾原理都差不多哈,或许你可以在它ill2cpp.so里面找到类似的函数。
卖血上网 发表于 2021-4-1 20:32
a1046830 发表于 2021-4-1 16:59
战争迷雾原理都差不多哈,或许你可以在它ill2cpp.so里面找到类似的函数。

菜鸡刚尝试用gg把迷雾去了,但好像是伪迷雾,看着像是地图全亮,但建筑物小兵还是没法显示出来
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-26 02:51

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表