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,無限制發魚雷

(已更新數個版本,鏈接作廢)
已刪

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,818評論 25 708
  • 使用到的工具 ApkToolBox.NET ReflectorReflexil(.NET程序編輯器) 怎么判定它是...
    風澈vio閱讀 9,169評論 1 69
  • 年初給自己訂了每年讀十本書的計劃,上周日去書店買了一大堆書,這個計劃還是很容易實現的。 購買的書有...
    449e4c01149c閱讀 675評論 0 1
  • “吱悠吱悠”石碓和木槌摩擦的聲音,在幽靜的胡同里格外的清晰,谷雨節氣,胡同口的槐樹盛開著潔白的花兒,槐花的香氣彌著...
    藥十一味閱讀 438評論 0 0
  • 六點多陪娃睡覺一覺醒來已是12點多……看看衣服還在身上,答應教孩子折愛心的事情也只能一拖再拖。叫醒孩子起床尿尿,而...
    wuyue6688閱讀 186評論 0 0