准备工具以及思路
1.il2cpp反编译工具il2cppdumper
2.010Editor
3.IDA Pro 7.0 绿色版
4.带语言高亮的记事本比如 Notepad++
5.一款支持打开APK的解压工具就行
6.一款U3Dil2cpp打包的游戏
思路:利用il2cppDumper获取到关键函数的地址后,使用IDA找到关键函数,分析汇编语言逻辑后,利用010Editor进行16进制文件编辑来实现修改。
判断是否是U3Dil2cpp打包的游戏
看到这个目录下有libil2cpp.so就是咯
此处注意:我们只分析armeabi-v7a文件夹下的libil2cpp.so,x86为intel架构,用模拟器测试要把x86这个文件夹删掉!不然修改不起作用
il2cppdumper的使用
将il2cpp.so连同assets\bin\Data\Managed\Metadata 目录下的global-metadata.dat 文件一起放进il2cppDumper目录。
运行il2cppDumper.exe,第一步,选择il2cpp.so
第二步,选择global-metadata.dat
进入到il2cppDumper控制台界面,这里按一下3就行
然后就会得到Dump.cs,包含关键函数的偏移信息
寻找关键函数
至于怎么寻找,我也没什么好方法,只能去猜测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
先附上两篇修改教程
Unity3D--Il2Cpp修改教程--二
这篇教程里已经说了如何修改此游戏的等级我就不在赘述【修改魔力的方法几乎可以说也一样】
UNITY3D Il2Cpp修改教程《电子机器人杀人事件》
因为我自己对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, [R0,#0x38]
.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+#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就可以直接修改指令
修改完成后的效果
.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, [R0,#(stru_273C.st_size+3 - 0x270F)]
.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
可以看到被我们修改过的指令对应的HEX都变成了棕色
打开010eidter 按 ctrl+g 跳转到对应地址
然后改成和IDA中一样的保存就行
修改解锁武器
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, [R1,#0xC]
.text:006138F8 CMP R2, #0
.text:006138FC BXEQ LR
.text:00613900 LDRB R0, [R1,#0x25]
.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, [R1,#0xC]
.text:006138F8 CMP R2, #0
.text:006138FC BXEQ LR
.text:00613900 NOP ; Keypatch modified this from:
.text:00613900 ; LDRB R0, [R1,#0x25]
.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