Android逆向练习 -- 分析和修改unity3d游戏

这是我第一次分析和修改unity3d游戏。
之前玩了一款很好玩的海战游戏,过程中遇到一个变态可以连续开炮,简直无敌变态,严重的破坏了公平性。所以本着研究的态度,第一次开始接触分析unity3d相关方面的知识。

简单的介绍下,游戏中有各种类型军舰可供使用,巡洋舰、驱逐舰、战列舰和航母,武器有主炮、副炮、近防炮、防空炮、鱼类、飞机等。炮弹是有填充cd时间的,发射完一次需要冷却x秒。然而遇到开挂的那货根本就没cd,连续发射,骚的要死,打的我等屁滚尿流。。。


好吧进入正题,这里就不介绍怎么分辨是不是unity3d类型的游戏,因为这款游戏有再明显不过的提示了。

第一步

解压apk,dnSpy加载Assembly-CSharp.dll。我这里还是使用虚拟机,毕竟Mac上的相关软件不好找。一打开程序,很陌生,不过工具的用法基本都大同小异。

展开目录看看,这里没有加密,类、方法还有一些成员反编译后整体还是很清晰的,只有一部分类名处理成了AEAHEOPPMLM类似的不易阅读的东西。

第二步 -- 定位代码

因为是第一次接触,所以没有着急的先按照那些教程进行搜索,而是挨个看了看里面的内容。虽然有些类名被处理了,但有些仍然保留着非常容易识别的名字,通过名字可以大概猜出该类实现的功能。(一般的教程是提示按照什么coin、cash、attack、hit等等关键字来搜索)

看的差不多了就来了感觉,还是用通用的关键词搜索方法试一下。

既然炮弹发射完后需要重新填充,那么必定有个地方来处理重新填充弹药的逻辑,这时候脑海就突然蹦出reloading这词。在FPS的游戏中,经常能听到这,就是换弹夹时候。抱着试一试的态度搜索一下reload,发现了可疑的函数GetReloadProgress,并且这个函数是在名叫TurretInstance类中。还是不着急直接看GetReloadProgress的具体逻辑,因为TurretInstance这个名字太引人注目了,翻译成傻白就是炮塔的实例。。。应该预览下这个类都写了哪些方法。

展开列表,发现了更可疑的关键字CanShotNow,并且还是布尔型的。

看字面意思就是问现在能不能射?等等不要停!!!跟进去看看再说

又跟进看了看IsReloaded和IsAimed方法,凭感觉应该是这里没错了。

    public bool IsReloaded()
    {
        return this.GetReloadProgress() >= 1f;
    }
    public bool IsAimed()
    {
        return this.COKKPADBCCI;
    }

这里我让CanShotNow()和 IsReloaded()一直返回true就好了,期望结果是没有reload,可以一直射。

第三步 修改

看教程是说比较常用的方式之一是编辑IL指令


IL指令是啥,没接触过啊。。。不过咱不怕,可以现学嘛。查了一下,发现其实和之前接触过的汇编很像,也是各种指令集。先跟着代码练一下

    public bool IsReloaded()
    {
        return this.GetReloadProgress() >= 1f;
    }
    
ldarg.0  # 将索引为 0 的参数加载到计算堆栈上
call instance folat32 TurretInstance::GetReloadProgress()  # 调用方法
ldc.r4  1  # 将所提供的 float32 类型的值作为 F (float) 类型推送到计算堆栈上。这里提供的是1
clt.un    # 比较无符号值,v1<v2则将1推送到计算堆栈上;反之将0推送到计算堆栈上
ldc.i4.0  # 将整数值 0 作为 int32 推送到计算堆栈上。
ceq  # 比较值,相等将1推送到计算堆栈上;不相等将0推送到计算堆栈上
ret  从当前方法返回,并将返回值

逻辑少,所以很清晰,也很容易弄明白。我们需要让它直接返回true,两行就够了,其他没用的指令全都删掉。

ldc.i4.1
ret

点击确定后可以看到反编译的结果直接是return true。


同样,也把CanShotNow()改掉。然后生成新的dll,打包回原apk中。

第四步 实践出真知

还是实践出真知,把“解带回原方程验证”。改完后得实际上手试试,感觉有点小激动。进入游戏,选择对战,然后开炮。。。果然,没有了重新填充弹药的cd时间,可以连续开炮了。这feel也是骚的要死。

后续更新

1、修改耐久度

玩儿的时候发现战舰还是有耐久度的,每打完一局需要用黄金来修复。还是关键词搜索 -- durability


前面那个方法非常可疑。

    public int GetDurability(string LENDAPGEFMF)
    {
        DateTime dateTime = SavedData.FHMGLEEKCKM.GetDateTime(CFPGGHAEJJK.CFNJBKJBIGJ(LENDAPGEFMF), default(DateTime));
        int @int = SavedData.FHMGLEEKCKM.GetInt(CFPGGHAEJJK.PFCLKBDALLH(LENDAPGEFMF), 0);
        int num = (int)((UnbiasedTime.Instance.Now() - dateTime).TotalSeconds * 0.013888888888888888);
        return Mathf.Clamp(@int + num, 0, 100);
    }

看这里基本确定就是我们要找的耐久度了,调用了c#的Mathf.Clamp方法,限定值是0到100之间,正好符合战舰耐久度0%~100%。那么我们只需要让@int + num的值大于100就好了。IL指令还不太熟,最后改成了这样,不过也符合需求。


打包apk,重新安装,再怎么玩发现耐久度都是100%了

2、升级武器时间

ShipData类中包含了许多可以修改的东西,其中就包括了武器升级时间。

public static readonly ObscuredInt[] sWeaponUpgradingTime = new ObscuredInt[]

还有这里,都是int型,直接改为return 0就好

3、完成一局后的系统奖励

LevelEndScreenRewardPackage:可以看到变量igahmbihpph分别对应Gold、Dollards、Rank。而这三个值都是int型,随便改

private void Start()
    {
        if (this.mIsPremium)
        {
            this.mPremiumPercents.text = "+" + 50 + "%";
        }
        string str = LanguageManager.Instance.GetTextValue("Gold").ToUpper();
        string str2 = LanguageManager.Instance.GetTextValue("Dollars").ToUpper();
        string str3 = LanguageManager.Instance.GetTextValue("Rank").ToUpper();
        bool flag = GamePlaySceneController.FHMGLEEKCKM.GetGamePlayState() == NIGDGPAKCDD.Victory;
        int igahmbihpph = AGDOBJHOIIP.PGBFJJGGCPJ(ickpehnnhba, this.mIsPremium);
        int igahmbihpph2 = AGDOBJHOIIP.IHCCOBMBINK(ickpehnnhba, this.mIsPremium);
        int igahmbihpph3 = AGDOBJHOIIP.IFBBNMJIKMI(ickpehnnhba, this.mIsPremium);
        this.mGoldAmount.text = "<size=18><color=#FF7900FF>" + str + "</color></size>\n" + LGLPBMGKBCA.CCFBDCGHAPD(igahmbihpph);
        this.mDollarsAmount.text = "<size=18><color=#FF7900FF>" + str2 + "</color></size>\n" + LGLPBMGKBCA.CCFBDCGHAPD(igahmbihpph2);
        this.mRankAmount.text = "<size=18><color=#FF7900FF>" + str3 + "</color></size>\n" + LGLPBMGKBCA.CCFBDCGHAPD(igahmbihpph3);
    }

然而这里并不管用,仔细分析了一下,这块只是展示逻辑,并不负责更新数据,继续查找。在LevelEndScreenRewardPage中找到了update()逻辑:还是一样,那几个int对应着gold、dollars、EXP。通过这里的修改后就实现了最终的修改

// LevelEndScreenRewardPage
// Token: 0x06002738 RID: 10040 RVA: 0x00116F14 File Offset: 0x00115114
private void Update()
{
    this.IIDIAKCBILF += Time.deltaTime;
    bool flag = CLIFCIJHHAF.FHMGLEEKCKM.OIDFGGEIMPJ().NNAINKAPABO();
    if (!flag && this.IIDIAKCBILF > this.mRegularRewardStateAppearDelay && !this.mRegularRewardState.activeSelf)
    {
        this.mRegularRewardState.SetActive(true);
    }
    if (flag && this.IIDIAKCBILF > this.mPremiumRewardStateAppearDelay && !this.mPremiumRewardState.activeSelf)
    {
        this.mPremiumRewardState.SetActive(true);
    }
    this.mRegularReward.alpha = ((!flag) ? 1f : 0.3f);
    this.mPremiumReward.alpha = ((!flag) ? 0.3f : 1f);
    this.mGetPremiumButton.SetActive(!flag);
    if (!flag && !this.ACFGGDLFKEA)
    {
        this.ACFGGDLFKEA = true;
        bool ickpehnnhba = GamePlaySceneController.FHMGLEEKCKM.GetGamePlayState() == NIGDGPAKCDD.Victory;
        int odjcphnbchd = AGDOBJHOIIP.PGBFJJGGCPJ(ickpehnnhba, false);
        int fapkfekpjic = AGDOBJHOIIP.IHCCOBMBINK(ickpehnnhba, false);
        int niljkdkplfa = AGDOBJHOIIP.IFBBNMJIKMI(ickpehnnhba, false);
        this.BAKMLFAIIMI(odjcphnbchd, fapkfekpjic, niljkdkplfa);
    }
    if (flag && !this.DPNKFCEJPNL)
    {
        this.DPNKFCEJPNL = true;
        bool ickpehnnhba2 = GamePlaySceneController.FHMGLEEKCKM.GetGamePlayState() == NIGDGPAKCDD.Victory;
        int odjcphnbchd2 = AGDOBJHOIIP.PGBFJJGGCPJ(ickpehnnhba2, true);
        int fapkfekpjic2 = AGDOBJHOIIP.IHCCOBMBINK(ickpehnnhba2, true);
        int niljkdkplfa2 = AGDOBJHOIIP.IFBBNMJIKMI(ickpehnnhba2, true);
        this.BAKMLFAIIMI(odjcphnbchd2, fapkfekpjic2, niljkdkplfa2);
    }
}

更新:.dll文件,请自行打包回apk中

  1. 解锁所有舰船
  2. 炮弹reload时间为1秒
  3. 舰船耐久度永远100%
  4. 升级舰船时间为0
  5. 战斗奖励翻倍,包括金钱、黄金、勋章
  6. 鱼雷数量增加到单侧500,无限制发鱼雷

(已更新数个版本,链接作废)
已删

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,362评论 25 707
  • 使用到的工具 ApkToolBox.NET ReflectorReflexil(.NET程序编辑器) 怎么判定它是...
    风澈vio阅读 8,941评论 1 69
  • 年初给自己订了每年读十本书的计划,上周日去书店买了一大堆书,这个计划还是很容易实现的。 购买的书有...
    449e4c01149c阅读 658评论 0 1
  • “吱悠吱悠”石碓和木槌摩擦的声音,在幽静的胡同里格外的清晰,谷雨节气,胡同口的槐树盛开着洁白的花儿,槐花的香气弥着...
    药十一味阅读 413评论 0 0
  • 六点多陪娃睡觉一觉醒来已是12点多……看看衣服还在身上,答应教孩子折爱心的事情也只能一拖再拖。叫醒孩子起床尿尿,而...
    wuyue6688阅读 178评论 0 0