小游戲 Brain Test 3 大腦測(cè)試3 跳過付費(fèi)內(nèi)容 無限內(nèi)購
以下是【bjwj007】分享的內(nèi)容全文:
小游戲 Brain Test 3 大腦測(cè)試3 跳過付費(fèi)內(nèi)容 無限內(nèi)購
游戲?qū)儆趇l2cpp,現(xiàn)在很多的安卓游戲都采用了這個(gè)打包。對(duì)于這類游戲,常見的改法是修改dex的入口,在入口處添加一個(gè)調(diào)用代碼,每次啟動(dòng)都自動(dòng)修改sp的long值,這樣就打到了無限金幣的目的。還有另一種改法就是直接修改so文件,通過修改底層代碼,可以實(shí)現(xiàn)的玩法也比較多,比如不減反增、定死數(shù)值、免費(fèi)購物、全部解鎖等。但是相對(duì)的也比較麻煩,對(duì)于沒有匯編經(jīng)驗(yàn)的萌新來說,還是寫個(gè)Java實(shí)在。
軟件樣品為當(dāng)前最新版1.50.0。
準(zhǔn)備工具
1、IDA32
2、Il2CppDumper
想不到這游戲已經(jīng)出到第三部了,前兩部我還發(fā)過帖子如何修改無限提示,不過是改dex的。現(xiàn)在換另一種方法,改so破解游戲。
游戲風(fēng)格比較適合小朋友,有些關(guān)卡我實(shí)在想不通為什么是這樣……
游戲有廣告,怎么去掉我就不說了,用幸運(yùn)破解器一鍵搞定就好了,來來去去都是那幾個(gè)熟面孔。

先看一下游戲界面,第一次玩游戲的時(shí)候會(huì)送50個(gè)電燈泡,正常情況下會(huì)越用越少,那么就會(huì)有相減的邏輯,修改so的時(shí)候可以利用相反的指令讓燈泡越來越多,也就是不減反增。
下好Il2CppDumper后,順序打開相應(yīng)的文件,libil2cpp、global-metadata.dat。等待分析完成后打開dump.cs文件備用。
接著ida打開so文件,選擇左上角運(yùn)行如下兩個(gè)腳本文件,靜靜等待加載完畢即可。電腦差的會(huì)比較久,可以先去喝杯奶茶再回來看看


之前說過這類游戲的數(shù)值一般儲(chǔ)存在sp里面,位于com.unicostudio.braintest3.v2.playerprefs.xml,打開后他是長這樣的:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?> <map> <int name="cross_promo_id" value="61" /> <string name="unity.player_session_count">5</string> <int name="Screenmanager%20Resolution%20Height" value="2220" /> <string name="unity.player_sessionid">4976473052650692822</string> <int name="Gdpr_Control" value="1" /> <string name="NotificationHelper.Scheduled">273129443%7C1094985734</string> <int name="__UNITY_PLAYERPREFS_VERSION__" value="1" /> <int name="Screenmanager%20Resolution%20Width" value="1080" /> <string name="unity.cloud_userid">95a0dbb4b1309fcf094060f513e8d42d</string> <string name="playerData">%7B%22coins%22%3A5%2C%22OnCoinsChanged%22%3A%7B%7D%2C%22gems%22%3A0%2C%22OnGemsChanged%22%3A%7B%7D%2C%22hammers%22%3A0%2C%22OnHammersChanged%22%3A%7B%7D%2C%22adsRemoved%22%3Afalse%2C%22OnAdsRemovedChanged%22%3A%7B%7D%2C%22completedLevels%22%3A%5B1%2C2%2C3%2C4%2C5%2C7%2C8%2C9%2C10%2C11%2C12%5D%2C%22lastReachedLevel%22%3A13%2C%22lastPlayedLevel%22%3A13%2C%22hintsOpened%22%3A%7B%226%22%3A%5B0%5D%2C%227%22%3A%5B0%2C1%5D%7D%2C%22OnHintsOpenedChanged%22%3A%7B%7D%2C%22flags%22%3A%5B%22new_level_popup_showed%22%2C%22bonus_Bonus1_skipped%22%2C%22hint_LevelTricky1_0%22%2C%22hint_LevelTricky1_1%22%2C%22tricky_LevelTricky1_played%22%2C%22hint_Make_Potion_1%22%2C%22hint_Alchemy_1%22%5D%2C%22selectedPets%22%3A%7B%22cat%22%3A-1%2C%22dog%22%3A-1%2C%22bird%22%3A-1%2C%22frog%22%3A-1%7D%2C%22unlockedPets%22%3A%7B%22cat%22%3A%5B%5D%2C%22dog%22%3A%5B%5D%2C%22bird%22%3A%5B%5D%2C%22frog%22%3A%5B%5D%7D%2C%22activeActionLevel%22%3A1%2C%22LastReachedLevel%22%3A13%7D</string> <int name="Screenmanager%20Fullscreen%20mode" value="-1" /> <int name="rate_shown" value="1" /> <int name="shareClaimed" value="1" /> </map>
playerData里的coins就是電燈泡的數(shù)量,gems應(yīng)該是鉆石。coins%22%3A5,這個(gè)5就是數(shù)量,當(dāng)前為5。

然后搜索dump.cs里的playerData,只有一個(gè)結(jié)果,那么這里就是就是破解的關(guān)鍵入口。其中g(shù)et_Coins、get_Gems這兩個(gè)都是比較常見的代碼,如果你不知道關(guān)鍵字可以先搜這兩個(gè),看看有沒有相關(guān)信息。
// Methods
// RVA: 0x13D954C Offset: 0x13D954C VA: 0x13D954C public int get_Coins() { }
// RVA: 0x13D14DC Offset: 0x13D14DC VA: 0x13D14DC public void set_Coins(int value) { }
// RVA: 0x13D9554 Offset: 0x13D9554 VA: 0x13D9554 public int get_Gems() { }
// RVA: 0x13D75B4 Offset: 0x13D75B4 VA: 0x13D75B4 public void set_Gems(int value) { }
// RVA: 0x13D955C Offset: 0x13D955C VA: 0x13D955C public int get_Hammers() { }
// RVA: 0x13D9564 Offset: 0x13D9564 VA: 0x13D9564 public void set_Hammers(int value) { }
// RVA: 0x13D9604 Offset: 0x13D9604 VA: 0x13D9604 public bool get_AdsRemoved() { }
// RVA: 0x13D960C Offset: 0x13D960C VA: 0x13D960C public void set_AdsRemoved(bool value) { }
// RVA: 0x13D96B8 Offset: 0x13D96B8 VA: 0x13D96B8 public int get_LastReachedLevel() { }
// RVA: 0x13D96C0 Offset: 0x13D96C0 VA: 0x13D96C0 public void set_LastReachedLevel(int value) { }
// RVA: 0x13D974C Offset: 0x13D974C VA: 0x13D974C public void SetHintOpen(int level, int hintIndex) { }
// RVA: 0x13D998C Offset: 0x13D998C VA: 0x13D998C public bool GetHintOpen(int level, int hintIndex) { }
// RVA: 0x13D9A88 Offset: 0x13D9A88 VA: 0x13D9A88 public void .ctor() { }
先來看Coins(),有兩個(gè)結(jié)果,public int get_Coins()和public void set_Coins(int value),get_Coins根據(jù)地址跳轉(zhuǎn)到0x13D954C,我查看了一下引用,結(jié)果為0,沒有函數(shù)引用這個(gè)地方,如果修改這里的話十有八九是沒什么用的。

接著繼續(xù)看set_Coins,跳轉(zhuǎn)到地址0x13D14DC。英文意思是設(shè)置、顯示數(shù)值。破解過java的應(yīng)該知道,方法名中有set_xx,get_xx,應(yīng)該是改get,而不是set,set這個(gè)函數(shù)基本上有傳入?yún)?shù),所以不能直接修改這里,這里只是用來顯示數(shù)值的。但是剛才的get找不到引用,所以只能另尋方法。

通過查看set_Coins的交叉引用,可以確定是哪里調(diào)用了顯示,如上圖紅框,分別是幾種扣費(fèi)地址。隨便選一個(gè),匯編如下:
il2cpp:01324714 ; --------------------------------------------------------------------------- il2cpp:01324714 MOV R0, #8 //函數(shù)地址 il2cpp:01324718 LDR R7, [R0] //r0地址讀取放入r7,此時(shí)r7是剩余的電燈泡 il2cpp:0132471C BL HintPanel$$get_cost //獲取商品價(jià)格,需要花費(fèi)多少電燈泡 il2cpp:01324720 MOV R6, R0 //把價(jià)格存入r6 il2cpp:01324724 BL sub_2B6FD4 il2cpp:01324728 ; --------------------------------------------------------------------------- il2cpp:01324728 il2cpp:01324728 loc_1324728 ; CODE XREF: sub_1324350+C4↑j il2cpp:01324728 SUB R1, R7, R6 //r1=r7-r6,此時(shí)r1是剩余的電燈泡 il2cpp:0132472C MOV R0, R5 //r5傳入r0,然后進(jìn)入BL il2cpp:01324730 MOV R2, #0 //初始化 il2cpp:01324734 BL PlayerData$$set_Coins //進(jìn)行顯示 il2cpp:01324738 MOV R0, #0 il2cpp:0132473C BL DataManager$$get_playerData il2cpp:01324740 MOV R5, R0 il2cpp:01324744 CMP R0, #0 il2cpp:01324748 BNE loc_1324750 il2cpp:0132474C BL sub_2B6FD4
v13 = DataManager__get_playerData(0); v14 = v13; if ( !v13 ) sub_2B6FD4(); v15 = *(v13 + 8); cost = HintPanel__get_cost(); PlayerData__set_Coins(v14, v15 - cost, 0); v17 = DataManager__get_playerData(0); if ( !v17 ) sub_2B6FD4();
偽代碼比較容易理解,有個(gè)減號(hào),很容易看出這是扣費(fèi)的地方。

所以把5個(gè)函數(shù)的調(diào)用,全部由減法改成加法指令(add)即可越用越多,其它的貨幣也是一樣的思路,就不一一舉例了。
本站不存儲(chǔ)任何資源文件,敬請(qǐng)周知!
本網(wǎng)站采用 BY-NC-SA 協(xié)議進(jìn)行授權(quán) 轉(zhuǎn)載請(qǐng)注明原文鏈接:小游戲 Brain Test 3 大腦測(cè)試3 跳過付費(fèi)內(nèi)容 無限內(nèi)購

侵權(quán)舉報(bào)/版權(quán)申訴



