U3D游戏《东方新世界》Il2Cpp破解详细教程
本帖最后由 CrazyNut 于 2018-11-10 23:57 编辑## 准备工具以及思路
1.(http://https://www.52pojie.cn/thread-817300-1-1.html)
2.(https://www.52pojie.cn/thread-684119-1-1.html)
3.(https://www.52pojie.cn/thread-675251-1-1.html)
4.[带语言高亮的记事本比如 Notepad++](https://notepad-plus-plus.org/download/v7.5.8.html)
5.一款支持打开APK的解压工具就行
6.一款U3Dil2cpp打包的游戏
**思路:利用il2cppDumper获取到关键函数的地址后,使用IDA找到关键函数,分析汇编语言逻辑后,利用010Editor进行16进制文件编辑来实现修改。**
### 判断是否是U3Dil2cpp打包的游戏
![](http://wx3.sinaimg.cn/mw690/78128aa9gy1fx33mnlejej208d08umxl.jpg)
看到这个目录下有libil2cpp.so就是咯
**此处注意:我们只分析armeabi-v7a文件夹下的libil2cpp.so,x86为intel架构,用模拟器测试要把x86这个文件夹删掉!不然修改不起作用**
## il2cppdumper的使用
将il2cpp.so连同assets\bin\Data\Managed\Metadata 目录下的global-metadata.dat 文件一起放进il2cppDumper目录。
![](http://wx4.sinaimg.cn/mw690/78128aa9gy1fx34wro66tj20gs03smxd.jpg)
运行il2cppDumper.exe,第一步,选择il2cpp.so
![](http://wx1.sinaimg.cn/mw690/78128aa9gy1fx351q2nsgj20md0cw3z2.jpg)
第二步,选择global-metadata.dat
![](http://wx3.sinaimg.cn/mw690/78128aa9gy1fx351qoqzbj20m60crmxc.jpg)
进入到il2cppDumper控制台界面,这里按一下3就行
![](http://wx3.sinaimg.cn/mw690/78128aa9gy1fx351t3tq6j20rl0efglq.jpg)
然后就会得到Dump.cs,包含关键函数的偏移信息
![](http://wx3.sinaimg.cn/mw690/78128aa9gy1fx351u3afvj20yo06m752.jpg)
### 寻找关键函数
至于怎么寻找,我也没什么好方法,只能去猜测GameMaker会给什么样的方法名
比如血量可以搜索HP,health这种关键词,魔力值搜索MP,金钱搜索Money等
我通过多次搜索找到了PlayerStats的类,里面包含了很多玩家信息
~~~
public class PlayerStats // TypeDefIndex: 3306 //玩家信息
{
// Methods
public void .ctor(); // 0x473A24
public void writeAllRoleStats(XmlWriter xmlWriter); // 0x473BC4
public void readToRoleDic(XmlNode xmlNode); // 0x474AC0
private void addDataString(string origin, string add); // 0x47557C
public BasicPlayerData get_CurrentBPData(); // 0x46564C
public int get_playerLevel(); // 0x469114 //获取玩家等级
public void set_playerLevel(int value); // 0x469140
public int get_playerXp(); // 0x467024 //获取玩家经验
public void set_playerXp(int value); // 0x4690B4
public int get_nextXpLevel(); // 0x4690E8 //获取玩家升级所需经验
public void set_nextXpLevel(int value); // 0x469174
public int get_maxHealth(); // 0x467074 /获取玩家最大血量
public void set_maxHealth(int value); // 0x47571C
public int get_currentHealth(); // 0x467050 //获取玩家当前血量
public void set_currentHealth(int value); // 0x4676E8
public int get_strengthByLevelUp(); // 0x475744 //获取力量值,其实就是攻击
public void set_strengthByLevelUp(int value); // 0x475770
public int get_velByLevelUp(); // 0x4757A4 //获取移动速度
public void set_velByLevelUp(int value); // 0x4757D0
public int get_staminaByLevelUp(); // 0x475804 //获取体力值,其实就是血量
public void set_staminaByLevelUp(int value); // 0x475830
public int get_dexterityByLevelUp(); // 0x475864
public void set_dexterityByLevelUp(int value); // 0x475890
public int get_mp(); // 0x467098
public void set_mp(int value); // 0x467710
public int get_maxMp(); // 0x4670BC //获取最大魔力值
}
~~~
还有一个叫GodManager的类= = 我猜是GM方便自己的
~~~
public class GodManager : NBMng // TypeDefIndex: 3469
{
// Methods
public void .ctor(); // 0x6137C0
public bool get_ShowAllMap(); // 0x6138C8//开图
public bool get_OpenAllEquip(); // 0x6138EC //解锁武器
public int get_SurviveWave(); // 0x613910
public string get_enemyName(); // 0x613928
public int get_testMapId(); // 0x6139F0
private void Start(); // 0x613A08
public override void abs_init(); // 0x613A0C
public override void abs_whenAllMngLoaded(); // 0x613A98
private void test(); // 0x613A9C
private void Update(); // 0x613B58
}
~~~
方法后面的注释就是方法对应的地址,至于为什么是那个方法= = 看的懂英语就行
当然这里面肯定还有能修改以达到破解目的不过我没发现的方法,大家可以自己尝试
## IDA中分析如何修改libil2cpp.so
先附上两篇修改教程
(https://www.52pojie.cn/thread-818304-1-1.html)
这篇教程里已经说了如何修改此游戏的等级我就不在赘述【修改魔力的方法几乎可以说也一样】
(https://www.52pojie.cn/thread-814250-1-1.html)
因为我自己对ARM也不熟不是很会分析代码,就给大家说一下自己的理解
### 修改最大血量
可以从上面找函数的图中看到
~~~
public int get_maxHealth(); // 0x467074 //获取玩家最大血量
~~~
**打开IDA拖入libil2cpp.so**
**按G跳转到对应地址血量0x467074**
~~~
.text:00467074 STMFD SP!, {R11,LR}
.text:00467078 MOV R11, SP
.text:0046707C BL loc_46564C
.text:00467080 CMP R0, #0
.text:00467084 LDRNE R0,
.text:00467088 LDMNEFD SP!, {R11,PC}
.text:0046708C BL loc_C186B4
.text:00467090 MOV LR, PC
.text:00467094 B loc_477490
~~~
按下F5看一下伪代码
~~~
int sub_467074()
{
int v0; // r0
v0 = (loc_46564C)();
if ( !v0 )
{
(loc_C186B4)();
JUMPOUT(&loc_477490);
}
return *(v0 + 56);
// 可以看到这里最后返回了一个INT
//既然这个方法是 get_maxHealth,返回值肯定就是最大血量
//所以我们只需要修改返回值就行
}
~~~
**IDA里面伪代码窗口右键 按 Copy to assembly 就会把伪代码和ARM对应起来
出现下图的效果**
~~~
.text:00467074 STMFD SP!, {R11,LR}
.text:00467078 MOV R11, SP
.text:0046707C ; 4: v0 = (loc_46564C)();
.text:0046707C BL loc_46564C //可以在这里给R0的值进心修改
.text:00467080 ; 5: if ( !v0 )
.text:00467080 CMP R0, #0
.text:00467084 ; 10: return *(v0 + 56); //这里可以看出来下面两句就在返回,而ARM中返回值一般是R0,我们在上面给R0一个自己想要的值就行
.text:00467084 LDRNE R0, //将R0+#0x38赋值给R0 ,我们把这句给 NOP 掉
.text:00467088 LDMNEFD SP!, {R11,PC}
.text:0046708C ; 7: (loc_C186B4)();
.text:0046708C BL loc_C186B4
.text:00467090 ; 8: JUMPOUT(&loc_477490);
.text:00467090 MOV LR, PC
.text:00467094 B loc_477490
~~~
**IDA中点击想修改的命令按ctrl+alk+K就可以直接修改指令**
![](http://wx1.sinaimg.cn/mw690/78128aa9gy1fx3758ip30j20ao0akt8r.jpg)
修改完成后的效果
~~~
.text:00467074 STMFD SP!, {R11,LR}
.text:00467078 MOV R11, SP
.text:0046707C MOV R0, #0x270F ; Keypatch modified this from:
.text:0046707C ; BL loc_46564C
.text:00467080 CMP R0, #0
.text:00467084 NOP ; Keypatch modified this from:
.text:00467084 ; LDRNE R0,
.text:00467088 LDMNEFD SP!, {R11,PC}
.text:0046708C BL loc_C186B4
.text:00467090 MOV LR, PC
.text:00467094 B loc_477490
~~~
伪代码
~~~
signed int sub_467074()
{
return 9999; //现在直接就返回9999,血量就算修改好了
}
~~~
### 在010editer中修改并保存
打开IDA的HEX窗口 按G跳转到 之前修改过的命令的地址
比如之前修改了
~~~
.text:0046707C MOV R0, #0x270F ;//下图就跳转到0046707C
~~~
![](http://wx2.sinaimg.cn/mw690/78128aa9gy1fx37rqdo12j20qy07dt9l.jpg)
可以看到被我们修改过的指令对应的HEX都变成了棕色
打开010eidter按 ctrl+g 跳转到对应地址
![](http://wx3.sinaimg.cn/mw690/78128aa9gy1fx37vtoaorj20px0cwwh1.jpg)
然后改成和IDA中一样的保存就行
![](http://wx1.sinaimg.cn/mw690/78128aa9gy1fx37vuuuo2j20i806p3zg.jpg)
### 修改解锁武器
~~~
public bool get_OpenAllEquip(); // 0x6138EC //解锁武器
~~~
**所以IDA中跳转到 0x6138EC 代码如下**
~~~
.text:006138EC sub_6138EC ; CODE XREF: .text:006EF8B4↓p
.text:006138EC ; DATA XREF: .data.rel.ro:00EA0CD0↓o
.text:006138EC MOV R1, R0
.text:006138F0 MOV R0, #0 //r0 = 0
.text:006138F4 LDRB R2,
.text:006138F8 CMP R2, #0
.text:006138FC BXEQ LR
.text:00613900 LDRB R0,
.text:00613904 CMP R0, #0
.text:00613908 MOVNE R0, #1 //通过上面的判断 决定是否让R0=1
.text:0061390C BX LR
.text:0061390C ; End of function sub_6138EC
~~~
**伪代码**
~~~
signed int __fastcall sub_6138EC(int a1)
{
int v1; // r1
signed int result; // r0
v1 = a1;
result = 0;
if ( *(_BYTE *)(v1 + 12) )
{
result = *(unsigned __int8 *)(v1 + 37);
if ( *(_BYTE *)(v1 + 37) )
result = 1;
}
return result;
}
~~~
~~~
public bool get_OpenAllEquip(); // 0x6138EC
//通过这个可以知道 返回值是一个bool
//而上面伪代码的返回值却是一个INT
//这个时候用屁股就应该想到0代表False而1代表True
//所以我们需要把返回值也就是R0改成1
~~~
**修改后如下**
~~~
.text:006138EC sub_6138EC ; CODE XREF: .text:006EF8B4↓p
.text:006138EC ; DATA XREF: .data.rel.ro:00EA0CD0↓o
.text:006138EC MOV R1, R0
.text:006138F0 NOP ; Keypatch modified this from:
.text:006138F0 ; MOV R0, #0
.text:006138F4 LDRB R2,
.text:006138F8 CMP R2, #0
.text:006138FC BXEQ LR
.text:00613900 NOP ; Keypatch modified this from:
.text:00613900 ; LDRB R0,
.text:00613904 NOP ; Keypatch modified this from:
.text:00613904 ; CMP R0, #0
.text:00613908 MOV R0, #1; Keypatch modified this from:
.text:00613908 ; MOVNE R0, #1
.text:0061390C BX LR
.text:0061390C ; End of function sub_6138EC
~~~
~~~
signed int __fastcall sub_6138EC(signed int result)
{
if ( *(_BYTE *)(result + 12) ) //这个判断其实也可以nop掉 已经没用了
result = 1; //反正下面 让返回值 为1 返回的为TRUE就行
return result;
}
~~~
**然后和修改血量同理在010中修改保存就行**
**现实地图和这个的修改方式一样不再赘述**
## 替换修改的So文件进安装包
**最后一步没啥讲的,怎么解压出来的怎么替换进去 **
**看来还是需要签名的,用Androidkiller签一下名就行了**
**成品地址:https://www.52pojie.cn/thread-818591-1-1.html**
**有兴趣的自己去改攻击力魔力什么的 有帮助到你给个免费评分噢 谢谢** txf446989400 发表于 2018-11-11 13:57
COCOS2djs.so 你有办法能获得包含关键函数的偏移信息吗?
这个都不是u3d的游戏= = 我没去了解过- - 不过似乎还是有这个东西的反编译工具 CrazyNut 发表于 2018-11-12 01:24
这个都不是u3d的游戏= = 我没去了解过- - 不过似乎还是有这个东西的反编译工具
有吗?我没找到,你有的话能不能发给我一份,万分感谢!! 辛苦了,大佬终于编辑完了 ╰Tang 发表于 2018-11-10 20:51
辛苦了,大佬终于编辑完了
{:301_971:}试了下那个MarkDown编辑器 不是很好用{:301_999:} 感谢分享教程 这个必须顶 辛苦了,大佬终于编辑完了 感谢分享 感谢分享 受教了,虚心学习 路过帮大神顶一下。。。。