簡單破解旅行青蛙

最近一款佛系養娃(誤)蛙的遊戲突然爆紅,我也試着玩了一下,結果被圈粉了。有蛙當然要曬,但是看朋友圈,別人的蛙出去拍的照片都是光鮮亮麗,呼朋喚友的,自家的蛙卻只能單人窮遊。說到底是當爹的沒錢害的,但是沒錢沒關係,咱喜歡瞎折騰。下面咱就來搞搞這個小遊戲。

首先,得拿到這個小遊戲的apk。我這是通過adb pull下來的,你也可以從應用市場下載。

改apk的後綴名爲rar,用壓縮軟件打開。大致看一下,可以看出來就是一個unity遊戲。那麼就把assets\bin\Data\Managed下的Assembly-CSharp.dll拿出來。咱們後面分析修改的就是這個dll文件了。


網上找了一下,發現dnSpy(dnSpy 是一款針對 .NET 程序的逆向工程工具)十分好用。打開dnSpy,將上面的dll拖入空白界面。主界面如下:


接下來開始分析吧。玩過遊戲的知道,遊戲的主要貨幣是三葉草,還有抽獎券也比較常用。


大致就是這種感覺,啥也買不起。另外一點比較尷尬,就是這個遊戲不支持中文,不過好在我們還能看懂葉、券、足三個字。這兩段日語的意思大致就是三葉草不夠、抽獎券不夠。

跑題了,反正我們已經知道了分析的切入點(就是那三個字)。dnSpy中Ctrl+Shift+K,出現全局搜索框,選擇數字/字符串,搜“足”字。正好出來兩個結果,1個對應抽獎券,1個對應三葉草。

首先看PushRollButton方法,雙擊定位到代碼處(dnSpy的好處就在於此,反編譯的代碼十分接近源代碼,很容易看懂)。PushRollButton很明顯指按下抽獎券那個轉輪按鈕的意思。如下就是該方法的反編譯代碼,可以看到一段熟悉的日語,看來是找對地方了。接下來就是看代碼了,這段代碼的意思很明確。首先判斷券的數量是否小於5(玩過遊戲的知道,5張券才能抽1次):小於就彈券不夠的日語框並結束這個方法,後面的抽獎的操作就不做了;大於等於就給你扣5張券,接着給你抽獎。

簡單地修改的話就是讓判斷條件恆爲假,不進入彈框步驟,並且抽獎不扣獎券。所以,我們可以將5和-5改爲0和0。獎券數量最少爲0,不會小於0,因此不會去彈框。每回抽獎前扣0張券。


那麼,如何改這個dll呢。dnSpy也提供修改dll文件的功能。在要修改的方法中右鍵選擇編輯IL指令。將ldc.i4.5改爲ldc.i4.0,將-5改爲0,點擊確定。再看代碼,已經改成功了。



券已經改完了,接下來是三葉草了。定位到SetInfoPanelData方法。方法有點長,我直接copy下來了。前面一堆大致是操作選擇物品相關的代碼,我們着重看if (SuperGameMaster.CloverPointStock() >= itemDataFormat.price) 後的代碼(從該判斷開始代表已經確定好要買的物品了)。該判斷可以明顯地看出就是將三葉草的數量和物品價格比較,不夠就彈框,夠就扣三葉草,一個套路。繼續分析,看到1個BuyItem方法,好了,還是一樣,想辦法不讓程序扣就行了。

public void SetInfoPanelData(int shopIndex, Vector3 pos)
	{
		if (shopIndex == -1)
		{
			this.unsetCursor();
			this.InfoPanel.GetComponent<InfoPanel>().SetInfoPanel(-1);
			return;
		}
		if (Mathf.Abs(this.flickMove) > this.S_FlickChecker.flickMin / 3f)
		{
			return;
		}
		if (this.selectShopIndex != shopIndex)
		{
			this.InfoPanel.GetComponent<InfoPanel>().SetInfoPanel(shopIndex);
			this.selectShopIndex = shopIndex;
			this.setCursor(pos);
			SuperGameMaster.audioMgr.PlaySE(Define.SEDict["SE_Cursor"]);
		}
		else
		{
			ShopDataFormat shopDataFormat = SuperGameMaster.sDataBase.get_ShopDB(shopIndex);
			ItemDataFormat itemDataFormat = SuperGameMaster.sDataBase.get_ItemDB_forId(shopDataFormat.itemId);
			if (itemDataFormat == null)
			{
				return;
			}
			if (!itemDataFormat.spend && SuperGameMaster.FindItemStock(itemDataFormat.id) != 0)
			{
				SuperGameMaster.audioMgr.PlaySE(Define.SEDict["SE_Cancel"]);
				return;
			}
			if (SuperGameMaster.CloverPointStock() >= itemDataFormat.price)
			{
				if (SuperGameMaster.FindItemStock(shopDataFormat.itemId) < 99)
				{
					base.GetComponent<FlickCheaker>().stopFlick(true);
					ConfilmPanel confilm = this.ConfilmUI.GetComponent<ConfilmPanel>();
					if (itemDataFormat.type == Item.Type.LunchBox)
					{
						confilm.OpenPanel_YesNo(string.Concat(new object[]
						{
							itemDataFormat.name,
							"\nを買いますか?\n(所持數\u3000",
							SuperGameMaster.FindItemStock(shopDataFormat.itemId),
							")"
						}));
					}
					else
					{
						confilm.OpenPanel_YesNo(itemDataFormat.name + "\nを買いますか?");
					}
					confilm.ResetOnClick_Yes();
					confilm.SetOnClick_Yes(delegate
					{
						confilm.ClosePanel();
					});
					confilm.SetOnClick_Yes(delegate
					{
						this.GetComponent<FlickCheaker>().stopFlick(false);
					});
					confilm.SetOnClick_Yes(delegate
					{
						this.BuyItem();
					});
					confilm.ResetOnClick_No();
					confilm.SetOnClick_No(delegate
					{
						confilm.ClosePanel();
					});
					confilm.SetOnClick_No(delegate
					{
						this.GetComponent<FlickCheaker>().stopFlick(false);
					});
				}
				else
				{
					base.GetComponent<FlickCheaker>().stopFlick(true);
					ConfilmPanel confilm = this.ConfilmUI.GetComponent<ConfilmPanel>();
					confilm.OpenPanel("もちものがいっぱいです");
					confilm.ResetOnClick_Screen();
					confilm.SetOnClick_Screen(delegate
					{
						confilm.ClosePanel();
					});
					confilm.SetOnClick_Screen(delegate
					{
						this.GetComponent<FlickCheaker>().stopFlick(false);
					});
				}
			}
			else
			{
				base.GetComponent<FlickCheaker>().stopFlick(true);
				ConfilmPanel confilm = this.ConfilmUI.GetComponent<ConfilmPanel>();
				confilm.OpenPanel("みつ葉が足りません");
				confilm.ResetOnClick_Screen();
				confilm.SetOnClick_Screen(delegate
				{
					confilm.ClosePanel();
				});
				confilm.SetOnClick_Screen(delegate
				{
					this.GetComponent<FlickCheaker>().stopFlick(false);
				});
			}
		}
	}

BuyItem方法:紅框是扣錢的操作,把price前面的負號去掉。買東西,程序給你三葉草,就是任性。

改的方法就是將neg給nop掉。

改完後:負號沒了。


Ctrl+Shift+S將更改後的dll文件存起來。

打開ApkIDE,將原始apk拖到APKIDE空白處打開。打開assets文件夾,將修改後的dll覆蓋原Assembly-CSharp.dll文件。編譯生成APK,安裝編譯後的APK就可以了。


效果:


後記:文中涉及到的IL指令沒有細說,因爲我也不會,自己查資料看着改吧。還有一些unity遊戲的漢化版可能也是通過這個方法來修改的吧。不過在這提醒一下大家,不要下載來歷不明的破解版或漢化版遊戲,很容易被人插入惡意代碼,竊取個人隱私、惡意扣費等。

所用工具鏈接:

修改了一下APKIDE的鏈接地址,我用的是七少月大佬的3.3.3增強版,不過他之後又更新了,有興趣的看着下吧。

APKIDE:https://www.pd521.com/thread-818-1-2.html

dnSpy:https://down.52pojie.cn/Tools/NET/dnSpy.zip


=============華麗分界線===============

哈哈,更新一下,前面說到改無限抽獎,但實際抽的時候老是抽到白玉,很煩。找了一下,搜“白玉”,可以看到抽獎概率,下面是我改過的概率,之前的是白:青:綠:紅:金=60:27:9:3:1。照着之前的辦法改吧,哈哈。


還有就是蛙回家時間,我試着找了相關代碼,太複雜了,改了幾次也沒成功。想玩快點的,照着網上說的修改系統時間來弄吧。

聲明:小白純屬瞎折騰蹭熱度。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章