《魔法密码》详细分析
本帖最后由 lzc090 于 2019-5-2 08:23 编辑前提:U3D游戏,请优先看懂这个贴子
U3D游戏《东方新世界》Il2Cpp破解详细教程
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
一.货币修改
public uint get_money(); // 0xA9BA5C
text:00A9BA5C sub_A9BA5C ; CODE XREF: .text:00A44198↑p
.text:00A9BA5C0C 00 D0 E5 LDRB R0,
.text:00A9BA601E FF 2F E1 BX LR
修改成:
text:00A9BA5C sub_A9BA5C
.text:00A9BA5C0F 02 E0 E3 MOV R0, #0xFFFFFFF
.text:00A9BA601E FF 2F E1 BX LR
--------------------------------------------------------------------------------------------------------------------------------------------------------
二.等级修改
public uint get_level(); // 0xA8D7D0
.text:00A8D7D0 sub_A8D7D0
.text:00A8D7D0 14 00 90 E5 LDR R0,
.text:00A8D7D4 1E FF 2F E1 BX LR
修改方式和货币一样,不过!!!!有个问题是,等级有了,但属性值可能没加上去,只是等级好看而已,没任何用处。因此我会介绍比较有用的经验修改
三.经验修改
经验修改1:
public uint get_exp(); // RVA: 0xA9BBE4 Offset: 0xA9BBE4
.text:00A9BBE4 sub_A9BBE4
.text:00A9BBE4 18 00 90 E5 LDR R0,
.text:00A9BBE8 1E FF 2F E1 BX LR
修改方式和金币一样,不过这个可能是获得的总经验
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
经验修改2:
public uint get_requiredExpToNextLevel(); // RVA: 0xA9BC1C-----------升级所需的必须经验
当看到
.text:00A9BC4C 14 10 94 E5 LDR R1, ---假设:当前等级
.text:00A9BC50 01 20 A0 E3 MOV R2, #1
.text:00A9BC54 63 30 A0 E3 MOV R3, #99
.text:00A9BC58 00 50 8D E5 STR R5,
.text:00A9BC5C 01 10 81 E2 ADD R1, R1, #1
.text:00A9BC60 9A FE 01 EB BL sub_B1B6D0
我就联想起dump文件里面的下列2个函数
public const uint minLevel = 1; // 0x0
public const uint maxLevel = 99; // 0x0
那么将
.text:00A9BC4C 14 10 94 E5 LDR R1,
修改成
.text:00A9BC4C 14 10 94 E5 MOV R1,99 当前等级不就变成了99了?
经过修改后发现,这个函数其实是每个等级需要的经验数,那么我们将修改的99换成1,每一级升级只需要等级1的经验也是100左右,我现在29级,还是只需要100.
------------------------------------------------------------------------------------------------------------------------------------------------------------
经验值修改3:
public uint get_restExpToNextLevel(); // RVA: 0xA8EBE4-----------------------这个是剩余升级的经验,就是经验条灰色部分。
.text:00A8EBE4 sub_A8EBE4
.text:00A8EBE4 10 4C 2D E9 STMFD SP!, {R4,R10,R11,LR}
.text:00A8EBE8 08 B0 8D E2 ADD R11, SP, #8
.text:00A8EBEC 00 40 A0 E1 MOV R4, R0;
.text:00A8EBF0 09 34 00 EB BL sub_A9BC1C ;
.text:00A8EBF4 18 10 94 E5 LDR R1, ------------获得的经验
.text:00A8EBF8 01 00 50 E0 SUBS R0, R0, R1 需求的经验-获得的经验,
.text:00A8EBFC 00 00 A0 93 MOVLS R0, #0 结果≤0那么,结果就等于0 (result = 0;)
.text:00A8EC00 10 8C BD E8 LDMFD SP!, {R4,R10,R11,PC} 如果是大于0,结果R0-R1(需求的经验-获得的经验)(result = v2 - v3;)==剩余升级经验(图片灰色部分)
伪代码:
unsigned int __fastcall sub_A8EBE4(int a1)
{
int v1; // r4
unsigned int v2; // r0
unsigned int v3; // r1
bool v4; // cf
unsigned int result; // r0
---------------------------------------------------------------------
v1 = a1;
v2 = sub_A9BC1C(a1); v1 = sub_A9BC1C(a1);
v3 = *(v1 + 24);
v4 = v2 >= v3; 修改后 v2 = v1 >= v1 - 1;
result = v2 - v3; result = 1;
if ( result == 0 || !v4 ) if ( !v2 )
result = 0; result = 0;
return result; return result;
}
那么我们只需要将结果都改成1,
将
.text:00A8EBF4 18 10 94 E5 LDR R1,
修改成:
.text:00A8EBF4 01 10 40 E2 SUB R1, R0, #1 ;
说明下:
.text:00A8EBF0 09 34 00 EB BL sub_A9BC1C ;
.text:00A8EBF4 01 10 40 E2 SUB R1, R0, #1 ; ------------获得的经验=需求经验-1
.text:00A8EBF8 01 00 50 E0 SUBS R0, R0, R1 --------- 需求的经验-获得的经验 =1 ,(v2 - v3)
.text:00A8EBFC 00 00 A0 93 MOVLS R0, #0 ------ 因为值=1不会<0,跳过
.text:00A8EC00 10 8C BD E8 LDMFD SP!, {R4,R10,R11,PC} 值大于0,那么结果就是R0-R1的值(result = v2 - v3;)
比如需求经验100 ,(SUB R1, R0, #1 ;)100-1=99, 获得的经验就是99, result = v2(100) - v3(99); 代码就变成了这个 result = 1;
修改测试并没有任何变化,可能有其他原因限制,因此这个修改为理论!
-----------------------------------------------------------------------------------------------------------------------------------------------------------
三.HP修改
public uint get_maxHp(); // RVA: 0xA9BC78 Offset: 0xA9BC78 ----------本函数在IDA对应的是loc_A9BC78
这是不能F5,所以就需要慢慢去查看了
找到关键的跳转:
.text:00A9BD34 AA 1C 02 EA B loc_B22FE4
跳转后找到的关键点:
.text:00B230D4 00 10 A0 E1 MOV R1, R0 ------------当前血量
.text:00B230D8 00 00 A0 E3 MOV R0, #0
.text:00B230DC 01 20 A0 E3 MOV R2, #1 --------------- MINHP
.text:00B230E0 0F 37 02 E3 MOV R3, #9999 ;------------------MAXHP
.text:00B230E4 00 40 8D E5 STR R4,
看到加粗的地方是不是又熟悉了,dump文件里面的函数
public const uint maxHp = 9999; // 0x0
只需要将
.text:00B230D4 00 10 A0 E1 MOV R1, R0 ------------当前血量
修改成
.text:00B230D4 00 10 A0 E1 MOV R1, 9999 ------------当前血量
----------------------------------------------------------------------------------------------------------------------------------------------------------
四.MP修改
看到MP和HP的汇编语言差不多因此尝试和HP一样的修改方式
public uint get_maxMp(); // RVA: 0xA9BD64 Offset: 0xA9BD64 ---------本函数在IDA对应的是loc_A9BD64
找到MP的相关代码
进入跳转.text:00A9BE20 FA 1C 02 EA B loc_B23210
找到关键位置
text:00B23318 0C 20 90 E5 LDR R2, -----------当前MP
.text:00B2331C 00 00 A0 E3 MOV R0, #0
.text:00B23320 E7 13 00 E3 MOV R1, #999
.text:00B23324 00 30 A0 E3 MOV R3, #0
.text:00B23328 63 A7 F4 EB BL sub_84D0BC ;
看到这里是不是又熟悉了,dump文件里面的函数
public const uint maxMp = 999; // 0x0
将
text:00B23318 0C 20 90 E5 LDR R2, -----------当前MP
修改成
text:00B23318 0C 20 90 E5 MOV R2, 999 -----------当前MP
发现没任何作用,那么函数就不是我们上面猜测的
我们分析下整体,找到了相关的点,所以就没吧所有的都贴出来了
.text:00B232E0 loc_B232E0 ; CODE XREF: .text:00B232CC↑j
.text:00B232E0 00 30 90 E5 LDR R3,
.text:00B232E4 08 10 A0 E1 MOV R1, R8
.text:00B232E8 04 20 90 E5 LDR R2,
.text:00B232EC 05 00 A0 E1 MOV R0, R5
.text:00B232F0 33 FF 2F E1 BLX R3
.text:00B232F4 00 10 A0 E1 MOV R1, R0
.text:00B232F8 00 00 A0 E3 MOV R0, #0
.text:00B232FC 00 20 A0 E3 MOV R2, #0
.text:00B23300 00 60 A0 E3 MOV R6, #0
.text:00B23304 06 9D FD EB BL sub_A8A724
.text:00B23308 00 50 A0 E1 MOV R5, R0
.text:00B2330C 08 00 94 E5 LDR R0,
.text:00B23310 00 00 50 E3 CMP R0, #0
.text:00B23314 0C 00 00 0A BEQ loc_B2334C
.text:00B23318 0C 20 90 E5 LDR R2, --我们之前改的是这个,但没用 unsigned int __fastcall sub_84D0BC {
.text:00B2331C 00 00 A0 E3 MOV R0, #0 if ( a2 < a3 ) ----R2<999
.text:00B23320 E7 13 00 E3 MOV R1, #999 a3 = a2;
.text:00B23324 00 30 A0 E3 MOV R3, #0 return a3;}
.text:00B23328 63 A7 F4 EB BL sub_84D0BC --刚好是sub,我们看下代码 这代码是求MP ,设当前MP:72/80,求这个80,
.text:00B2332C 00 30 A0 E1 MOV R3, R0 (R1, R0) 这就是上面的80也是MAXMP
.text:00B23330 00 00 A0 E3 MOV R0, #0 (R0, #0)
.text:00B23334 05 10 A0 E1 MOV R1, R5 (R2, #1) 当前MP
.text:00B23338 00 20 A0 E3 MOV R2, #0 (R3, #9999) MIXMP
.text:00B2333C 00 60 8D E5 STR R6,
有没发现蓝色这一段和上面HP的蓝色是同一个函数()里面的是HP的,不过位置变动了
位置调换一下
.text:00B23334 05 10 A0 E1 MOV R1, R5 (R1, R0)
.text:00B23330 00 00 A0 E3 MOV R0, #0 (R0, #0)
.text:00B23338 00 20 A0 E3 MOV R2, #0 (R2, #1)
.text:00B2332C 00 30 A0 E1 MOV R3, R0 (R3, #9999)
这样我们就知道要修改哪了
将
.text:00B23334 05 10 A0 E1 MOV R1, R5
修改成:
.text:00B23334 05 10 A0 E1 MOV R1, 999
将
.text:00B2332C 00 30 A0 E1 MOV R3, R0
修改成
.text:00B2332C 00 30 A0 E1 MOV R3, 999
----------------------------------------------------------------------------------------------------------------------------------------------------
五.无限体力
游戏回复体力的方式看广告或者是等待时间回复
无限体力1(跳过广告):
public bool get_skipAds(); // RVA: 0xA9D88C Offset: 0xA9D88C IDA对应的是loc_A9D88C,
同样无法看伪代码,不过查看过程中发现了这个关键点(代码很长就不全部贴出来了)
.text:00A9DB78 loc_A9DB78
.text:00A9DB74 00 60 A0 E3 MOV R6, #0
.text:00A9DB78
.text:00A9DB78 loc_A9DB78
.text:00A9DB78 06 00 A0 E1 MOV R0, R6
.text:00A9DB7C F0 8B BD E8 LDMFD SP!, {R4-R9,R11,PC}
将
.text:00A9DB78 06 00 A0 E1 MOV R0, R6
修改成
.text:00A9DB78 06 00 A0 E1 MOV R0, 1
同时抽取塔罗牌也是跳过广告的
修改体力2:
public uint get_motivation(); // RVA: 0xA8E98C Offset: 0xA8E98C
代码太长,提取关键地方
从伪代码可以开到,最后的结果是return (loc_A9C08C)(v1, HIDWORD(v7), v4, v3, v5, v6); 对应的就是loc_A9C08C,那么进去loc_A9C08C差看下
关键处:
前面的代码好像是计算时间的,不过我不确定就不在这里说了
.text:00A9C1B8 2F 69 DB EB BL j___udivsi3 ---F5return a1 / a2 血力格式 R5/R0
.text:00A9C1BC 05 50 80 E0 ADD R5, R0, R5 ------- 当前体力
.text:00A9C1C0 04 00 A0 E1 MOV R0, R4
.text:00A9C1C4 70 FC FF EB BL loc_A9B38C -------这个是上限MAX体力,后面会分析到
.text:00A9C1C8 00 00 55 E1 CMP R5, R0 ---判断当前体力是不是满
.text:00A9C1CC 05 00 A0 91 MOVLS R0, R5 -- R5<=R0,体力不满,当前体力为R5
当体力满时 ,当前体力=MAX体力 =R0
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
关键MAX体力跳转
.text:00A9C1C4 70 FC FF EB BL loc_A9B38C
关键跳转
.text:00A9B448 66 20 02 EA B loc_B235E8
关键点:
看到是不是有是熟悉的感觉
.text:00B236E0 00 10 A0 E1 MOV R1, R0; 当前体力MAX
.text:00B236E4 00 00 A0 E3 MOV R0, #0
.text:00B236E8 01 20 A0 E3 MOV R2, #1 体力限制MIN
.text:00B236EC E7 33 00 E3 MOV R3, #999 体力现在MAX
.text:00B236F0 00 40 8D E5 STR R4,
将
.text:00B236E0 00 10 A0 E1 MOV R1, R0
修改成
.text:00B236E0 E7 13 00 E3 MOV R1, #999
六.跳过战斗画面
public bool get_skipBattle(); // RVA: 0xA9DBC8 Offset: 0xA9DBC8
同样的跳过战斗过程修改和去广告也是一样的修改方式
七.修改角色属性
1.敏捷/防御(其实是同一个东西)
public uint get_agility(); // RVA: 0xA9BF3C Offset: 0xA9BF3C-------IDA对应的是loc_A9BE50
public uint get_defence(); // RVA: 0xA9BE50 Offset: 0xA9BE50
关键代码
同样我们会再底部看到一个跳转
.text:2B 1D 02 EA B loc_B234AC ;
进去后看到一个关键的地方
是不是和HP的一样,敏捷取值范围1<=敏捷<=99
.text:00B23468 00 10 A0 E1 MOV R1, R0
改成
.text:00B23468 00 10 A0 E1 MOV R1, 99
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
七.秒杀
秒杀修改方式有2种
1.修改主角本身的攻击力
2.修改怪物的血量为1
本游戏是主角挨打,伙伴攻击因此方式1的修改并不存在,我们修改方式2.
public uint get_hp(); // RVA: 0xB1141C Offset: 0xB1141C
.text:00B1141C sub_B1141C
.text:00B1141C 1C 00 90 E5 LDR R0,
.text:00B11420 1E FF 2F E1 BX LR
修改为
.text:00B1141C sub_B1141C
.text:00B1141C 1C 00 90 E5 mov R0, 1
.text:00B11420 1E FF 2F E1 BX LR
很多参数(暴击、闪避、无敌等)修改方式和上面介绍的差不多,多去看dump文件里面的参数就知道了,就不一一罗列出来了。
希望大家喜欢!
本帖最后由 lzc090 于 2019-1-16 20:51 编辑
878510 发表于 2019-1-16 20:25
..
在C之前是不是应该有更基础的东西
我啥都不懂呢
U3D打包模式有2种:
momo打包的游戏,源码是直接可以看到的,论坛也有很多入门的教程。
IL2是吧源码放到了so文件,反编译出来变成ARM语言的。需要再把ARM语言自己专为C#,所以难度会更大点,因此需要C#语言也需要ARM语言。
所以建议你从momo开始认识,这个帖子你就直接跳过,先去看MOMO的游戏教程 878510 发表于 2019-1-16 21:04
嗯
照着做都搞不定
去移动板块,查看那些无限金币帖子开始一步一步跟着做。
https://www.52pojie.cn/thread-846577-1-2.html
这个帖子就很适合入门学习,也写的非常清楚。如果看不懂,就需要多花点时间,记得是动手跟着做才有用。 支持原创……感谢楼主分享……。
最后一个小分分送上,请笑纳 看着可以哦 支持了。。。。 好教程,感谢楼主分享,学习下 精华帖预定{:1_932:} 感谢楼主的分享。支持原创 膜拜,感谢分享 好教程,楼主牛逼 感谢楼主分享,学习下