简单破解旅行青蛙

转载:https://blog.csdn.net/kalongkaya/article/details/79161734

最近一款佛系养娃(误)蛙的游戏突然爆红,我也试着玩了一下,结果被圈粉了。有蛙当然要晒,但是看朋友圈,别人的蛙出去拍的照片都是光鲜亮丽,呼朋唤友的,自家的蛙却只能单人穷游。说到底是当爹的没钱害的,但是没钱没关系,咱喜欢瞎折腾。下面咱就来搞搞这个小游戏。

首先,得拿到这个小游戏的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方法,好了,还是一样,想办法不让程序扣就行了。


 
 
  1. public void SetInfoPanelData(int shopIndex, Vector3 pos)
  2. {
  3. if (shopIndex == -1)
  4. {
  5. this.unsetCursor();
  6. this.InfoPanel.GetComponent<InfoPanel>().SetInfoPanel( -1);
  7. return;
  8. }
  9. if (Mathf.Abs( this.flickMove) > this.S_FlickChecker.flickMin / 3f)
  10. {
  11. return;
  12. }
  13. if ( this.selectShopIndex != shopIndex)
  14. {
  15. this.InfoPanel.GetComponent<InfoPanel>().SetInfoPanel(shopIndex);
  16. this.selectShopIndex = shopIndex;
  17. this.setCursor(pos);
  18. SuperGameMaster.audioMgr.PlaySE(Define.SEDict[ "SE_Cursor"]);
  19. }
  20. else
  21. {
  22. ShopDataFormat shopDataFormat = SuperGameMaster.sDataBase.get_ShopDB(shopIndex);
  23. ItemDataFormat itemDataFormat = SuperGameMaster.sDataBase.get_ItemDB_forId(shopDataFormat.itemId);
  24. if (itemDataFormat == null)
  25. {
  26. return;
  27. }
  28. if (!itemDataFormat.spend && SuperGameMaster.FindItemStock(itemDataFormat.id) != 0)
  29. {
  30. SuperGameMaster.audioMgr.PlaySE(Define.SEDict[ "SE_Cancel"]);
  31. return;
  32. }
  33. if (SuperGameMaster.CloverPointStock() >= itemDataFormat.price)
  34. {
  35. if (SuperGameMaster.FindItemStock(shopDataFormat.itemId) < 99)
  36. {
  37. base.GetComponent<FlickCheaker>().stopFlick( true);
  38. ConfilmPanel confilm = this.ConfilmUI.GetComponent<ConfilmPanel>();
  39. if (itemDataFormat.type == Item.Type.LunchBox)
  40. {
  41. confilm.OpenPanel_YesNo( string.Concat( new object[]
  42. {
  43. itemDataFormat.name,
  44. "\nを買いますか?\n(所持数\u3000",
  45. SuperGameMaster.FindItemStock(shopDataFormat.itemId),
  46. ")"
  47. }));
  48. }
  49. else
  50. {
  51. confilm.OpenPanel_YesNo(itemDataFormat.name + "\nを買いますか?");
  52. }
  53. confilm.ResetOnClick_Yes();
  54. confilm.SetOnClick_Yes( delegate
  55. {
  56. confilm.ClosePanel();
  57. });
  58. confilm.SetOnClick_Yes( delegate
  59. {
  60. this.GetComponent<FlickCheaker>().stopFlick( false);
  61. });
  62. confilm.SetOnClick_Yes( delegate
  63. {
  64. this.BuyItem();
  65. });
  66. confilm.ResetOnClick_No();
  67. confilm.SetOnClick_No( delegate
  68. {
  69. confilm.ClosePanel();
  70. });
  71. confilm.SetOnClick_No( delegate
  72. {
  73. this.GetComponent<FlickCheaker>().stopFlick( false);
  74. });
  75. }
  76. else
  77. {
  78. base.GetComponent<FlickCheaker>().stopFlick( true);
  79. ConfilmPanel confilm = this.ConfilmUI.GetComponent<ConfilmPanel>();
  80. confilm.OpenPanel( "もちものがいっぱいです");
  81. confilm.ResetOnClick_Screen();
  82. confilm.SetOnClick_Screen( delegate
  83. {
  84. confilm.ClosePanel();
  85. });
  86. confilm.SetOnClick_Screen( delegate
  87. {
  88. this.GetComponent<FlickCheaker>().stopFlick( false);
  89. });
  90. }
  91. }
  92. else
  93. {
  94. base.GetComponent<FlickCheaker>().stopFlick( true);
  95. ConfilmPanel confilm = this.ConfilmUI.GetComponent<ConfilmPanel>();
  96. confilm.OpenPanel( "みつ葉が足りません");
  97. confilm.ResetOnClick_Screen();
  98. confilm.SetOnClick_Screen( delegate
  99. {
  100. confilm.ClosePanel();
  101. });
  102. confilm.SetOnClick_Screen( delegate
  103. {
  104. this.GetComponent<FlickCheaker>().stopFlick( false);
  105. });
  106. }
  107. }
  108. }

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。照着之前的办法改吧,哈哈。


还有就是蛙回家时间,我试着找了相关代码,太复杂了,改了几次也没成功。想玩快点的,照着网上说的修改系统时间来弄吧。

声明:小白纯属瞎折腾蹭热度。


  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值