近期旅行青蛙這款游戲非常的火熱,周圍的朋友辞友、家人都養(yǎng)了一只小青蛙栅哀。看到網(wǎng)上有人說這款游戲可以直接逆向編譯称龙,沒有加密留拾;所以在搜索相關(guān)資料后花了一些時(shí)間進(jìn)行逆向分析與修改。這篇文章里鲫尊,我將介紹如何獲取稀有明信片的方法以及如何逆向修改代碼后得到破解版本的“旅行青蛙”游戲的方法痴柔。
獲取稀有明信片
引入
我們?cè)谕媛眯星嗤艿臅r(shí)候會(huì)發(fā)現(xiàn),大部分小青蛙寄回來的明信片都很類似疫向,即使有小伙伴(蝴蝶咳蔚、青蛙、螃蟹搔驼、小老鼠)谈火,大家的動(dòng)作都是很類似的。通過分析代碼可以知道舌涨,這些明信片是自動(dòng)合成的糯耍。意思就是,會(huì)有幾個(gè)不同的背景模板、幾個(gè)動(dòng)物的動(dòng)作模板谍肤,排列組合進(jìn)行合成啦租,生成普通明信片。而所謂的稀有明信片荒揣,就是非排列組合合成的篷角,而是單獨(dú)繪制的、精美的明信片系任。這里有稀有明信片的百度網(wǎng)盤下載地址 密碼:F338恳蹲。
步驟
下面介紹如何獲取明信片,首先我們需要Android版旅行青蛙的安裝包apk文件俩滥,網(wǎng)上有很多下載地址嘉蕾,或者直接從官方下載到手機(jī)里并用adb導(dǎo)出到電腦里來即可。不管你用什么方式霜旧,安裝包獲取后错忱,將后綴名更改為rar或者zip壓縮包的形式,再進(jìn)行解壓(解壓后應(yīng)該有assets挂据、lib以清、res等等文件夾),這些解壓后的文件實(shí)際上都是旅行青蛙游戲的代碼構(gòu)成部分崎逃。
然后我們需要用到Unity Studio掷倔,下載地址見這里,下載完成后直接在文件夾中運(yùn)行Unity Studio.exe个绍,左上角Load Folder勒葱,將../assets/bin/Data文件夾導(dǎo)入,會(huì)發(fā)現(xiàn)原來空的列表出現(xiàn)一大堆文件巴柿。按如圖所示切換到右邊的列表(Asset List):
接下來按照Size進(jìn)行排序蔓涧,基本上就是游戲的圖片構(gòu)成以及模板:
經(jīng)過長時(shí)間的搜尋會(huì)發(fā)現(xiàn)Size=525088就是我們要找的明信片形式:
由上述兩張圖可以看出冗茸,第一張為稀有明信片喳资、第二張就為普通模板(加上前面的一些小動(dòng)物可以構(gòu)成普通明信片)憎夷。我們先將所有Size=525088的圖片全部導(dǎo)出:
經(jīng)過一番本地的篩選铜秆,就可以獲得所有的稀有明信片了闷煤,這里放上3張:
逆向分析代碼
引入
這里之所以選用Andriod的旅行青蛙進(jìn)行逆向分析人断,一方面是因?yàn)槁眯星嗤艿腁ndriod比較好分析披泪、是最簡單的情況蜗侈,一般的Unity3D游戲都會(huì)用一些保護(hù)將dll腳本加密的篷牌,如果是這樣的話可能就需要調(diào)用hook函數(shù)來逐步分析、比較麻煩踏幻;另一方面是因?yàn)閕os版本的旅行青蛙枷颊,會(huì)用到ll2cpp,不如Andriod好分析(利用dnSpy可以直接分析Andriod版本的C#腳本,代碼易懂好分析)
dnSpy分析C#腳本
在獲取明信片的步驟里夭苗,我們已經(jīng)將旅行青蛙的安裝包apk文件進(jìn)行了rar解壓信卡,這里就不需要另外操作了。
然后我們需要用到dnSpy軟件進(jìn)行反編譯题造,dnSpy下載地址在這里傍菇。點(diǎn)擊dnSpy.exe即可啟動(dòng),我們需要查看的是游戲的C#腳本界赔,路徑為../assets/bin/Data/Managed/Assembly-CSharp.dll丢习。反編譯后直接就能顯示代碼:
當(dāng)然我們玩過游戲,應(yīng)該認(rèn)識(shí)游戲中的日文字淮悼,搜索讓我們輸入小青蛙姓名的句子(搜索的快捷鍵為Crtl+Shift+K)咐低,就可以在CallTutorial找到屏幕上顯示的句子和相應(yīng)的代碼:
修改“青蛙命名”句子的例子
我們先以修改為小青蛙取名字的句子做一個(gè)例子:在日文句子處右擊選擇編輯IL指令,熟悉的匯編語言就出現(xiàn)了M嘈取见擦!
我們可以看到位于164行處LDSTR(即Load String把字符串加壓入Evaluation Stack中)就是我們需要修改的日文句子,進(jìn)行修改:
修改為:
點(diǎn)擊確定羹令,回到C#腳本代碼界面就能看到原本的日文句子已經(jīng)成功被修改成為自己修改的句子了鲤屡,真機(jī)測(cè)試效果圖將放在結(jié)尾處。
修改三葉草數(shù)量
方法1:
分析代碼特恬,圖中黃色圈出的部分是在購買時(shí)相關(guān)的执俩。第一處為判斷,當(dāng)Clover(中文就是三葉草)的存儲(chǔ)量比商品item的價(jià)格要大的時(shí)候就可以執(zhí)行下面的操作癌刽;第二處是生成提示文本:"...是否要購買役首?",然后經(jīng)過SetOnClick事件為True之后显拜,就執(zhí)行第三處的BuyItem()函數(shù)衡奥。所以我們可以輕易的判斷出,三葉草的數(shù)量的減少肯定和BuyItem()函數(shù)這個(gè)函數(shù)有關(guān)远荠,跳轉(zhuǎn)到BuyItem()函數(shù)處:
圖中黃色圈住的部分調(diào)用了getCloverPoint(-itemDataFormat.price)矮固,之后的函數(shù)為GetItem(..,1),意思是商品item數(shù)加1譬淳,表示已購買档址;根據(jù)函數(shù)邏輯,黃色圈住的部分就是減少三葉草數(shù)量的函數(shù)邻梆,傳入的為-itemDataFormat.price守伸,為一個(gè)負(fù)值;那么我們前往getCloverPoint()函數(shù)看看:
傳入的負(fù)值與原來的三葉草數(shù)量相加浦妄,相當(dāng)于三葉草數(shù)量減少了商品價(jià)格的數(shù)尼摹,符合邏輯见芹。我們?nèi)粝胄薷哪_本,自然就要打破這個(gè)邏輯蠢涝,最容易想到的就是:既然買了商品會(huì)加上-itemDataFormat.price這個(gè)負(fù)值玄呛,若我們傳入的沒有這個(gè)負(fù)號(hào)不就可以了。所以右擊選擇編輯IL指令:
neg就是匯編指令中的負(fù)號(hào)的意思和二,將它nop掉就ok了:
點(diǎn)擊確定回到C#腳本看看效果:
可以看出負(fù)號(hào)已經(jīng)沒有了E锹痢!所以購買物品時(shí)就不會(huì)減少三葉草的數(shù)量惯吕,反而是增加了庭砍。
方法二:
其實(shí)方法一已經(jīng)足夠了,但是在閱讀源碼的時(shí)候混埠,發(fā)現(xiàn)調(diào)用CloverPointStock()函數(shù)時(shí)怠缸,不管是購買還是收獲三葉草,都是會(huì)返回savaData里的數(shù)值(經(jīng)過各種流程計(jì)算后的結(jié)果)钳宪,return的結(jié)果是這樣子的形式的:
既然代碼是返回經(jīng)過運(yùn)算的一個(gè)值揭北,呢不如直接返回一個(gè)特定的數(shù),同樣進(jìn)行修改:
修改為:(LDSFLD是 將靜態(tài)字段的值推送到計(jì)算堆棧上吏颖,后一級(jí)的LDFLD剛好構(gòu)成級(jí)與級(jí)之間的調(diào)用關(guān)系搔体,即savaData.CloverPoint)
返回的數(shù)值可以任選,比如我選的23333半醉;確定后看效果:
修改成功疚俱。為了方便,我采用了第二種方法缩多。
修改抽獎(jiǎng)券
抽獎(jiǎng)機(jī)制在旅行青蛙這款游戲中也是非常重要的呆奕,因?yàn)楦鶕?jù)抽獎(jiǎng)出來的珠子的顏色可以獲得小青蛙旅游時(shí)的加分,也決定了小青蛙前去的目的地衬吆。然而經(jīng)常券不夠是個(gè)問題梁钾。所以搜索(Ctrl+Shift+K)“券”或“足”這個(gè)字,就可以定位到抽獎(jiǎng)券代碼的位置:
黃色圈出的位置是說逊抡,如果獎(jiǎng)券數(shù)量小于5姆泻,就提示券不足文本,如果獎(jiǎng)券數(shù)量大于5就將獎(jiǎng)券數(shù)量減5然后執(zhí)行一次隨機(jī)抽球過程冒嫡,這個(gè)隨機(jī)抽球后面會(huì)進(jìn)行講解拇勃,這里先說怎么修改:我們可以將小于5更改為小于0,獎(jiǎng)券數(shù)量始終減0孝凌,這樣子就實(shí)現(xiàn)了即使獎(jiǎng)券數(shù)量為0仍然可以抽獎(jiǎng):
筆者在這里犯糊涂方咆,將小于5更改為了大于0...在獎(jiǎng)券為0的時(shí)候是可以抽獎(jiǎng)的,但是一旦小青蛙帶回來新的券使券的數(shù)量大于0胎许,就會(huì)出錯(cuò)抽不起來了峻呛。。這是個(gè)顯而易見的錯(cuò)誤辜窑,大家修改時(shí)注意一下就行了钩述。
提高中獎(jiǎng)概率
相信我們?cè)诔楠?jiǎng)的時(shí)候,大部分抽到的都是白玉穆碎,白玉是非常常見的牙勘,其他顏色的玉的顏色都很少見,筆者玩了2個(gè)月一個(gè)紅球或者黃球都沒有抽到所禀,分析代碼后發(fā)現(xiàn)問題在這里:
抽中白玉的概率為60%方面,抽中藍(lán)玉的概率為27%,抽中綠玉的概率為9%色徘,抽中紅玉的概率為3%恭金,抽中黃玉的概率為1%。褂策。横腿。可想而知斤寂,抽中非白色玉的概率是多么的小耿焊。。遍搞。
下面我們?cè)俜治鲆幌侣藓睿侨绾卫贸榍蚋怕食榍虻哪兀?br>
首先用num存儲(chǔ)0到PrizeBallsRank.RankMax中的任何一個(gè)值,用num2存儲(chǔ)各種玉的PrizeBalls的值溪猿,當(dāng)隨機(jī)選擇的num值比num2的值小的時(shí)候钩杰,就將result更新為這個(gè)num2的值,考慮到除了白玉之外的PrizeBalls的值都小的可怕诊县,最高也才為27榜苫,所以非白色玉的可能性非常的小。
我們要做的當(dāng)然就是將概率提升上去:
圖中0x3c翎冲、0x1B都是16進(jìn)制表示的數(shù)垂睬,表示的數(shù)分別為60、27抗悍,所以我們可以對(duì)整體進(jìn)行修改驹饺;值得一提的是,部分表示的數(shù)是在匯編指令中直接賦值的缴渊,比如ldc.i4.1赏壹,就是表示數(shù)1。按照16進(jìn)制進(jìn)行修改:
效果為:
由圖可見衔沼,概率大大的提升了r蚪琛昔瞧!
四葉草概率的提升
既然三葉草我們可以控制,呢么四葉草我們應(yīng)該可以控制菩佑,找了很長時(shí)間的代碼自晰,發(fā)現(xiàn)在CloverFarm文件中有關(guān)于三葉草、四葉草生成的方法:
代碼實(shí)際告訴我們稍坯,四葉草的生成與否是由bool型變量flag所決定的酬荞。如果flag為0的話,就生成三葉草瞧哟,執(zhí)行l(wèi)ist.Add(this.NewCloverObject(j,this.cloverList[j],list))混巧。如果flag為1的話,執(zhí)行一次生成四葉草程序list.Add(this.NewCloverObject(j,this.cloverList[j],list勤揩,true))咧党,然后flag再變?yōu)?;所以我們跳轉(zhuǎn)到生成四葉草程序去看看:
由圖中黃色圈出的部分可以看出陨亡,生成四葉草共有兩種判斷方式凿傅,首先是第一處的概率判斷,當(dāng)Random.Range(0f,10000f)比fourLeaf_percent*100f的概率小的時(shí)候数苫,就令cloverDataFormat.element=1聪舒,從而執(zhí)行第三處的四葉草生成程序箱残。好的止吁,那么我們看看fourLeaf_percent的數(shù)值是多少呢被辑?
由圖可見取值為1f敬惦,什么概念呢?就是變成四葉草的概率為1/100俄删;第二種的判斷方式為fourLeafFlag是否為1宏怔,利用的就是flag畴椰,同樣flag變?yōu)?的過程也是概率極小的斜脂。所以我們可以手動(dòng)更改概率,修改為:
這樣就將概率提升到80%了玷或。
當(dāng)然偏友,我們也可以將fourLeafFlag這個(gè)第二處的判斷條件更改為
if(true)
cloverDataFormat.element=1;
三葉草生成時(shí)間
網(wǎng)上之前提到了一個(gè)修改系統(tǒng)時(shí)間獲得三葉草的方法约谈,確實(shí)可行的原因在這里犁钟,旅行青蛙用的是timespan進(jìn)行生成三葉草的涝动,即device time-last harvest time
來判斷醋粟,旅行青蛙對(duì)于時(shí)間的檢測(cè)比較嚴(yán)格,因?yàn)樵谀M旅行系統(tǒng)部分的代碼中厦凤,我們可以知道较鼓,小青蛙的旅游和回家和timespan是相關(guān)的博烂,如果隨意修改系統(tǒng)時(shí)間影響的不僅僅是三葉草的生成速度禽篱,影響的還有小青蛙的模擬旅游系統(tǒng)馍惹。万矾。勤众。
而生成三葉草的確是和timespan相關(guān):
這里調(diào)用了一個(gè)Clamp函數(shù)们颜,我們跳轉(zhuǎn)過去看看:
我們知道Recyle就應(yīng)該是三葉草的循環(huán)生成程序,而value如果小于cloverSpanMin就讓它等于cloverSpanMin硫嘶;如果value大于cloverSpanMax沦疾,就讓它等于cloverSpanMax哮塞。而cloverSpanXXX的值為:
從函數(shù)中簡單理解就是忆畅,如果我們收割過三葉草后家凯,相當(dāng)于設(shè)置了一個(gè)計(jì)時(shí)器如失,我們的計(jì)時(shí)器默認(rèn)開始為cloverSpanMin=300褪贵,即5min竭鞍,直到cloverSpanMax=14400偎快,即4個(gè)小時(shí),所以長出一輪新的三葉草需要時(shí)間為4個(gè)小時(shí)裆馒。當(dāng)我們遠(yuǎn)超4個(gè)小時(shí)還沒有收三葉草的時(shí)候喷好,由于value會(huì)大于cloverSpanMax梗搅,所以程序會(huì)始終讓value=14400,從而不會(huì)出現(xiàn)三葉草一直在增長的現(xiàn)象荡短,修改最大時(shí)間的話就可以確保收獲的時(shí)間變短:
我修改為了360,即6min循環(huán)一次籍嘹。
關(guān)于模擬旅行系統(tǒng)
為什么叫旅行青蛙辱士,看了源碼后我想應(yīng)該會(huì)有所了解识补,關(guān)于模擬旅行系統(tǒng)的代碼占據(jù)了所有代碼的一半之多凭涂。開發(fā)者真的在這個(gè)旅行系統(tǒng)上花費(fèi)了很大的功夫G杏汀名惩!導(dǎo)致這部分的代碼比較復(fù)雜娩鹉,在這部分代碼中大量出現(xiàn)了生成日志log的函數(shù)弯予,顯示小青蛙當(dāng)前位置是在哪里锈嫩、參與了什么活動(dòng)呼寸、遇見了什么伙伴(這些在源碼中都是轉(zhuǎn)化為text寫到log里的),我估計(jì)是開發(fā)者們?yōu)榱瞬换煜麄€(gè)邏輯所做的一個(gè)措施吧河狐。甚牲。丈钙。雏赦。
我簡要的分析一下星岗,模擬旅行系統(tǒng)中最讓我吃驚的部分還是“圖論”部分,竟然涉及到了圖論允华,之前一直以為僅僅是根據(jù)時(shí)間判斷的而已靴寂,但是開發(fā)者卻真的是創(chuàng)建了結(jié)點(diǎn)與邊來表示旅行地點(diǎn):
上圖利用到了路徑算法百炬,代表真的是考慮到了旅行地點(diǎn)的變化剖踊。
此外德澈,模擬旅行系統(tǒng)還涉及到了地形因素梆造,如山澳窑、海摊聋、洞穴:
根據(jù)地形的不同麻裁,旅游時(shí)間會(huì)不同煎源、是否能到達(dá)目的地也是不同的。我認(rèn)為歇僧,模擬旅行系統(tǒng)其實(shí)就是借用圖論的架構(gòu)進(jìn)而表示旅行的時(shí)間诈悍,而所帶的物品侥钳、地形都會(huì)對(duì)旅行的時(shí)間有影響舷夺。而具體的模擬旅行系統(tǒng)所用的旅行時(shí)間也和在家休息時(shí)長给猾、上一次旅行時(shí)間有關(guān)耙册。而同時(shí),旅行時(shí)間也決定了所能去的地點(diǎn)蔓同、下一次回來的時(shí)間斑粱、下一次出游的時(shí)間脯爪、能獲得的明信片:
從這里我們就知道明信片的生成其實(shí)也是和地點(diǎn)尚揣、時(shí)間有關(guān)的快骗,圖片可以由地點(diǎn)的模板組合生成方篮,也就是普通明信片的生成方法。
但是匕得,模擬旅行系統(tǒng)可能還要復(fù)雜些耗跛。调塌。羔砾。先附上一段代碼:
可以看出開發(fā)者考慮了是否到達(dá)姜凄、目的地态秧、起始地申鱼、中途小青蛙是否會(huì)休息捐友、會(huì)遇到誰匣砖、是要生成普通明信片還是稀有明信片....猴鲫,開發(fā)者考慮了這些狀態(tài)拂共,并用enum枚舉量進(jìn)行了分析匣缘。
關(guān)于旅行系統(tǒng)肌厨,在寫博客的時(shí)候剛好看到一篇文章分析的非常非常全面柑爸,有興趣的可以借鑒閱讀一下表鳍。
反編譯生成Apk
當(dāng)然不會(huì)是只將修改過的dll腳本文件替換就行了譬圣,這樣因?yàn)閍pk的簽名會(huì)不一致厘熟,所以不能正確安裝绳姨。這里我們需要對(duì)apk進(jìn)行重新編譯飘庄,從而生成新的證書跪削。
這里我使用的是ApkIDE切揭,下載地址在這里
操作方法比較簡單廓旬,將原來的apk文件拖進(jìn)窗口內(nèi)孕豹,會(huì)自動(dòng)進(jìn)行解壓励背。將我們修改過的Assembly-CSharp.dll覆蓋原先的dll文件叶眉,然后編譯生成apk文件即可衅疙,生成步驟如下:
真機(jī)測(cè)試
測(cè)試結(jié)果如下:
教程寫的比較倉促绩郎,如果有什么錯(cuò)誤肋杖,會(huì)在日后進(jìn)行修改状植。轉(zhuǎn)載請(qǐng)申明出處浅萧,謝謝M莩帝簇!
----YunLambert