garfield028 发表于 2019-9-19 17:45

某商业街游戏修改

本帖最后由 garfield028 于 2019-9-19 18:52 编辑

最近在看论坛教程,学习一些移动安全上面的知识,索性也找了一个游戏学学修改。

我下载了一款游戏 -> Shopping Mall Tycoon_v1.1.2

通过查看APK文件,发现他是基于Unity开发,然后看到游戏已经打包成libil2cpp了。

依葫芦画瓢,使用 Il2CppDumperhttps://github.com/Perfare/Il2CppDumper 进行dump,so。

至于DUMP教程,其实论坛很多,比如https://www.52pojie.cn/thread-733757-1-1.html

然后剩下就是分析DUMP文件了。

首先,我们必须要进入游戏看看游戏的场景,这个步骤很重要,发现一些游戏的特性。

如图:



发现了游戏中货币至少3种,然后体验下基础玩法,最后看了一下钻石的获得方式。

得到如下结论:

建筑物升级: 4个 分支

1.可容纳人数
2.人均消费
3.消费速度
4.改造

然后钻石是通过观看广告

然后还有一些细节,就是离线后,可以挂机,也就是说,有离线收益。

总和上面的信息,我在DUMP.CS中搜索如下关键字

gold , money , diamond , face , house , watch

然后看看有没有开发者的接口

搜索 debugcontrol , gm , gamemaster , gmui , testui

我一共从dump.cs中找到了如下关键函数


// Namespace:
public class GameData : MonoBehaviour // TypeDefIndex: 3339
{
      // Fields
      省略

      // Methods
      public void .ctor(); // RVA: 0x1D6FE0 Offset: 0x1D6FE0
      private void Awake(); // RVA: 0x1D71EC Offset: 0x1D71EC
      private void Update(); // RVA: 0x1D794C Offset: 0x1D794C
      public void Init(); // RVA: 0x1D7284 Offset: 0x1D7284
      public bool Lock(int id); // RVA: 0x1D7F44 Offset: 0x1D7F44
      public string LockString(int id); // RVA: 0x1D85F0 Offset: 0x1D85F0
      public void GetExp(int getExp); // RVA: 0x1D6840 Offset: 0x1D6840
      public void UpdateGround(); // RVA: 0x1D90F4 Offset: 0x1D90F4
      public float GetExpProgress(); // RVA: 0x1D98EC Offset: 0x1D98EC
      public string GetExpPorgressString(); // RVA: 0x1D99E8 Offset: 0x1D99E8
      public void MoneyNotSave(MoneyType type, long number); // RVA: 0x1D9B64 Offset: 0x1D9B64
      public long GetMoney(MoneyType type); // RVA: 0x1D445C Offset: 0x1D445C
      public void SaveMoney(MoneyType type, long number); // RVA: 0x1CA70C Offset: 0x1CA70C
      public bool GetMoneyEnough(MoneyType type, long need); // RVA: 0x1D9FA0 Offset: 0x1D9FA0
      public long GetMoneyReally(MoneyType type); // RVA: 0x1D9FD8 Offset: 0x1D9FD8
      public void SaveThiefNum(); // RVA: 0x1DA020 Offset: 0x1DA020
      public void Saveuuid(); // RVA: 0x1DA094 Offset: 0x1DA094
      public void SaveGround(GroundItem item); // RVA: 0x1D9634 Offset: 0x1D9634
      public void GetGround(GroundItem ground, int groundIndex); // RVA: 0x1DA108 Offset: 0x1DA108
      public void SaveRole(int id); // RVA: 0x1D7AB0 Offset: 0x1D7AB0
      public int GetRole(int id); // RVA: 0x1D7BF0 Offset: 0x1D7BF0
      public long GetRoleBasePower(int id); // RVA: 0x1DA628 Offset: 0x1DA628
      public float GetHouseCapcity(int buildingId, int pro); // RVA: 0x1DA834 Offset: 0x1DA834
      public long GetHouseCapcityGold(int buildingId, int pro); // RVA: 0x1DA9D8 Offset: 0x1DA9D8容纳人数 升级需要资金
      public long GetHouseCost(int buildingId, int pro); // RVA: 0x1DABC8 Offset: 0x1DABC8
      public long GetHouseCostGold(int buildingId, int pro); // RVA: 0x1DADB8 Offset: 0x1DADB8消费数 升级需要资金
      public float GetHouseTime(int buildingId, int pro); // RVA: 0x1DAFA8 Offset: 0x1DAFA8
      public long GetHouseTimeGold(int buildingId, int pro); // RVA: 0x1DB150 Offset: 0x1DB150消费速度 升级需要资金
      public long GetHouseReformGold(int buildingId, int buildingLevel); // RVA: 0x1DB340 Offset: 0x1DB340店铺升级 需要资金
      public int GetPeopleNum(); // RVA: 0x1DB530 Offset: 0x1DB530
      public int GetMaxCap(int buildingId); // RVA: 0x1DB640 Offset: 0x1DB640
      public void SaveAchieve(int id, int condition); // RVA: 0x1DB660 Offset: 0x1DB660
      public int GetAchieve(int id); // RVA: 0x1D7CC4 Offset: 0x1D7CC4
      public void SaveBuildingsBuild(int id); // RVA: 0x1DB770 Offset: 0x1DB770
      public int GetBuildingsBuild(int id); // RVA: 0x1D7D98 Offset: 0x1D7D98
      public void SaveGroundNum(); // RVA: 0x1D9878 Offset: 0x1D9878
      public void SaveSaleDay(); // RVA: 0x1C8808 Offset: 0x1C8808
      public void SaveOpenLevel(int value); // RVA: 0x1DB8B0 Offset: 0x1DB8B0
      public void SaveOpenHP(int value); // RVA: 0x1DB924 Offset: 0x1DB924
      public void SaveWatchTimes(); // RVA: 0x1D7E6C Offset: 0x1D7E6C
      public void SaveWatchGetCount(); // RVA: 0x1D7ED8 Offset: 0x1D7ED8
      public void SaveSellExp(int needExp); // RVA: 0x1DB998 Offset: 0x1DB998
      public int GetThisNeedExp(); // RVA: 0x1D8E18 Offset: 0x1D8E18
      public int GetNextNeedExp(); // RVA: 0x1DB99C Offset: 0x1DB99C
      public long GetWatchDiamond(); // RVA: 0x1DBA84 Offset: 0x1DBA84看广告获得钻石数量
      public void ChangeScene(int scene); // RVA: 0x1DBAA0 Offset: 0x1DBAA0
      public void GetPlaying(); // RVA: 0x1DBB14 Offset: 0x1DBB14
      public void SavePlaying(); // RVA: 0x1DBF18 Offset: 0x1DBF18
      public static void SetVector3(string key, Vector3 vector); // RVA: 0x1DC440 Offset: 0x1DC440
      public static Vector3 GetVector3(string key); // RVA: 0x1DBDD4 Offset: 0x1DBDD4
      public void UpdateGiveRole(); // RVA: 0x1D8F00 Offset: 0x1D8F00
      public void UpdateVersion(); // RVA: 0x1DC560 Offset: 0x1DC560
      private static void .cctor(); // RVA: 0x1DC6C8 Offset: 0x1DC6C8
}

public sealed class HOUSE : IMessage`1<HOUSE>, IMessage, IEquatable`1<HOUSE>, IDeepCloneable`1<HOUSE> // TypeDefIndex: 3382
{
      // Fields
      省略

      // Properties
      省略

      // Methods
       // RVA: 0x16BE2C Offset: 0x16BE2C
      public void .ctor(); // RVA: 0x3F8888 Offset: 0x3F8888
       // RVA: 0x16BE3C Offset: 0x16BE3C
      public void .ctor(HOUSE other); // RVA: 0x3F8964 Offset: 0x3F8964
      public static MessageParser`1<HOUSE> get_Parser(); // RVA: 0x3F8C50 Offset: 0x3F8C50
      public static MessageDescriptor get_Descriptor(); // RVA: 0x3F8CDC Offset: 0x3F8CDC
      private MessageDescriptor Google.Protobuf.IMessage.get_Descriptor(); // RVA: 0x3F8E84 Offset: 0x3F8E84
       // RVA: 0x16BE4C Offset: 0x16BE4C
      public HOUSE Clone(); // RVA: 0x3F8EFC Offset: 0x3F8EFC
      public int get_ID(); // RVA: 0x3F8F6C Offset: 0x3F8F6C
      public void set_ID(int value); // RVA: 0x3F8F74 Offset: 0x3F8F74
      public int get_Name(); // RVA: 0x3F8F7C Offset: 0x3F8F7C
      public void set_Name(int value); // RVA: 0x3F8F84 Offset: 0x3F8F84
      public int get_CapacityStart(); // RVA: 0x3F8F8C Offset: 0x3F8F8C
      public void set_CapacityStart(int value); // RVA: 0x3F8F94 Offset: 0x3F8F94
      public float get_Capcity(); // RVA: 0x3F8F9C Offset: 0x3F8F9C
      public void set_Capcity(float value); // RVA: 0x3F8FA4 Offset: 0x3F8FA4
      public long get_CapcityGold(); // RVA: 0x3F8FAC Offset: 0x3F8FAC
      public void set_CapcityGold(long value); // RVA: 0x3F8FB4 Offset: 0x3F8FB4
      public float get_CapcityMulti(); // RVA: 0x3F8FC4 Offset: 0x3F8FC4
      public void set_CapcityMulti(float value); // RVA: 0x3F8FCC Offset: 0x3F8FCC
      public long get_EarningStart(); // RVA: 0x3F8FD4 Offset: 0x3F8FD4
      public void set_EarningStart(long value); // RVA: 0x3F8FDC Offset: 0x3F8FDC
      public float get_ShopPower(); // RVA: 0x3F8FEC Offset: 0x3F8FEC
      public void set_ShopPower(float value); // RVA: 0x3F8FF4 Offset: 0x3F8FF4
      public long get_ShopPowerGold(); // RVA: 0x3F8FFC Offset: 0x3F8FFC
      public void set_ShopPowerGold(long value); // RVA: 0x3F9004 Offset: 0x3F9004
      public float get_ShopPowerMulti(); // RVA: 0x3F9014 Offset: 0x3F9014
      public void set_ShopPowerMulti(float value); // RVA: 0x3F901C Offset: 0x3F901C
      public int get_TimeStart(); // RVA: 0x3F9024 Offset: 0x3F9024
      public void set_TimeStart(int value); // RVA: 0x3F902C Offset: 0x3F902C
      public float get_WaitTime(); // RVA: 0x3F9034 Offset: 0x3F9034
      public void set_WaitTime(float value); // RVA: 0x3F903C Offset: 0x3F903C
      public long get_WaitTimeGold(); // RVA: 0x3F9044 Offset: 0x3F9044
      public void set_WaitTimeGold(long value); // RVA: 0x3F904C Offset: 0x3F904C
      public float get_WaitTimeMulti(); // RVA: 0x3F905C Offset: 0x3F905C
      public void set_WaitTimeMulti(float value); // RVA: 0x3F9064 Offset: 0x3F9064
      public string get_Reform(); // RVA: 0x3F906C Offset: 0x3F906C
      public void set_Reform(string value); // RVA: 0x3F9074 Offset: 0x3F9074
      public long get_ReformGold(); // RVA: 0x3F90F4 Offset: 0x3F90F4
      public void set_ReformGold(long value); // RVA: 0x3F90FC Offset: 0x3F90FC
      public float get_ReformMulti(); // RVA: 0x3F910C Offset: 0x3F910C
      public void set_ReformMulti(float value); // RVA: 0x3F9114 Offset: 0x3F9114
      public string get_Desc(); // RVA: 0x3F911C Offset: 0x3F911C
      public void set_Desc(string value); // RVA: 0x3F9124 Offset: 0x3F9124
      public int get_OpenLevel(); // RVA: 0x3F91A4 Offset: 0x3F91A4
      public void set_OpenLevel(int value); // RVA: 0x3F91AC Offset: 0x3F91AC
      public string get_BuildValue(); // RVA: 0x3F91B4 Offset: 0x3F91B4
      public void set_BuildValue(string value); // RVA: 0x3F91BC Offset: 0x3F91BC
      public string get_BuildImage(); // RVA: 0x3F923C Offset: 0x3F923C
      public void set_BuildImage(string value); // RVA: 0x3F9244 Offset: 0x3F9244
      public int get_Score(); // RVA: 0x3F92C4 Offset: 0x3F92C4
      public void set_Score(int value); // RVA: 0x3F92CC Offset: 0x3F92CC
      public long get_NeedGold(); // RVA: 0x3F92D4 Offset: 0x3F92D4
      public void set_NeedGold(long value); // RVA: 0x3F92DC Offset: 0x3F92DC
       // RVA: 0x16BE5C Offset: 0x16BE5C
      public override bool Equals(object other); // RVA: 0x3F92EC Offset: 0x3F92EC
       // RVA: 0x16BE6C Offset: 0x16BE6C
      public bool Equals(HOUSE other); // RVA: 0x3F9370 Offset: 0x3F9370
       // RVA: 0x16BE7C Offset: 0x16BE7C
      public override int GetHashCode(); // RVA: 0x3F96B4 Offset: 0x3F96B4
       // RVA: 0x16BE8C Offset: 0x16BE8C
      public override string ToString(); // RVA: 0x3F9B1C Offset: 0x3F9B1C
       // RVA: 0x16BE9C Offset: 0x16BE9C
      public void WriteTo(CodedOutputStream output); // RVA: 0x3F9BA4 Offset: 0x3F9BA4
       // RVA: 0x16BEAC Offset: 0x16BEAC
      public int CalculateSize(); // RVA: 0x3FA598 Offset: 0x3FA598
       // RVA: 0x16BEBC Offset: 0x16BEBC
      public void MergeFrom(HOUSE other); // RVA: 0x3FAC10 Offset: 0x3FAC10
       // RVA: 0x16BECC Offset: 0x16BECC
      public void MergeFrom(CodedInputStream input); // RVA: 0x3FADEC Offset: 0x3FADEC
      private static void .cctor(); // RVA: 0x3FB3CC Offset: 0x3FB3CC
       // RVA: 0x16BEDC Offset: 0x16BEDC
      private static HOUSE <_parser>m__0(); // RVA: 0x3FB4A8 Offset: 0x3FB4A8
}
关键函数我已经标红,然后标红的都是我自己修改的函数。

接下来就是修改了,修改的教程论坛也可以找到很多

比如:https://www.52pojie.cn/forum.php?mod=viewthread&tid=818304

我就不多做介绍了。

上面 4个升级函数差不多,在IDA中,我修改如下位置:
这4个升级函数代码段还是比较长,他要计算等级 周期 然后计算最后的花费。
但我们只需要关注最后计算的结果:



关键就在这里了:
.text:001DAF6C 3F 40 FD EB                                                   BL            j___floatdisf
.text:001DAF70 10 0A 00 EE                                                   VMOV            S0, R0
.text:001DAF74 10 4A 01 EE                                                   VMOV            S2, R4
.text:001DAF78 01 0A 20 EE                                                   VMUL.F32      S0, S0, S2
.text:001DAF7C 10 0A 10 EE                                                   VMOV            R0, S0
.text:001DAF80 9B 3F FD EB                                                   BL            j___fixsfdi
.text:001DAF84 F0 8D BD E8                                                   LDMFD         SP!, {R4-R8,R10,R11,PC}

其实,我也不怎么看的懂ARM汇编,猜就是把一个浮点付给R0,然后,我只需要把一个固定值付给R0就好了。
所以我改成了MOV R0,0x0 ,对应的机器码:00 00 A0 E3 ,然后用UE等BINHEX编辑工具,找到OFFSET去修改就好了。然后我把4个升级函数都改成0。

接下来就是Diamond观看函数,差不多的原理,我就不截图了,直接贴代码段
这个是watch diamon 我修改后的代码段: 我把修改了的地方也标红了
.text:001DBA84 3C 00 90 E5                                                   LDR             R0,
.text:001DBA88 00 01 80 E0                                                   ADD             R0, R0, R0,LSL#2
.text:001DBA8C 05 00 80 E2                                                   ADD             R0, R0, #5
.text:001DBA90 FF 00 50 E3                                                   CMP             R0, #0xFF
.text:001DBA94 FF 0F 0F E3                                                   MOVLT         R0, #0xFFFF
.text:001DBA98 40 1E A0 E1                                                   MOV             R1, R0,ASR#28   
.text:001DBA9C 1E FF 2F E1                                                   BX            LR

这里我说一下,我第一次修改后,发现不对,因为对ARM汇编不熟悉,所以忽略了ASR这个指令。。。。然后修改了操作位数后,结果正确了。但是这个并不完美,因为还是要观看一次广告。。。

剩下就是收益了,具体方法和上面差不多,我就不继续废话了。

最后,给一个几个结果截图吧:
然后分享一下修改后的APK




修改后的APK包:

下载:https://www.lanzouj.com/i69kc9g 密码:heb3


初学者,很多地方都是摸着过河,不清楚的,说错的,大家多多包涵:)

onihot 发表于 2019-9-20 14:23

本帖最后由 onihot 于 2019-9-20 14:26 编辑

这类教程得多支持,感谢楼主分享技术!我也是很喜欢研究游戏修改,尤其汇编,可以学习到的东西比较多,我现在改汇编遇到最大的问题,不知怎么增加指令,比如说我改个金钱或者经验倍数,遇到双字节的thumb指令根本无从下手修改,因为增加指令会导致很多指令偏移地址。

jyhh09 发表于 2019-9-20 22:27

garfield028 发表于 2019-9-20 16:08
我是用的模拟器,我没有android真机。。抱歉啊。。。 看看能不能网上搜下方法,或者去签个名之类的。。。 ...
你模拟器有广告加载吗?什么模拟器?这个版本游戏怎么都是广告!!

啊笨 发表于 2019-9-20 11:57

哈哈,不错不错,游戏下着玩试试!

a16565001 发表于 2019-9-20 12:19

显示广告没有加载

cyj158 发表于 2019-9-20 13:05

高手技术贴,顶起!{:301_987:}

wuyaqing981206 发表于 2019-9-20 13:21

顶顶顶,萌新路过

信仰自由 发表于 2019-9-20 13:30

华为手机想玩玩但是显示没有证书

jyhh09 发表于 2019-9-20 14:26

把广告获取的判断值该一下就完美了

starlancer 发表于 2019-9-20 14:48

游戏下着玩试试!

啊笨 发表于 2019-9-20 15:59

安装失败,提示安装包损坏!
页: [1] 2 3
查看完整版本: 某商业街游戏修改