Soul Knight破解小记

最近TapTap上有一款Roguelike的射击游戏Soul Knight火了,下载玩了一会儿,果然很好玩。但是只有骑士一个角色,其他角色都需要付费解锁,角色的养成也需要大量的宝石,对我这样的手残党来说真是很尴尬。但是Soul Knight是单机游戏,存档甚至购买记录都保存在本地,于是乎,偷个懒看看能不能破解,好玩了再入手正式版支持一下开发者:)。

一.尝试破解il2cpp版本

首先我去TapTap下载的最新的版本,解包后发现已经il2cpp过了,Managed文件夹下传统的Dll被编译到Lib下的libil2cpp.so了,这下子不能再修改Assembly-CSharp.dll了。之前没试过il2cpp的游戏修改,但是还是决定进行一系列的尝试,参考了IL2Cpp Dumper的教程Dump出了所有的类接口,下面是一个角色控制器:

// Namespace: 
public class C01Controller // TypeDefIndex: 3435
{
	// Fields
	private RGHand hand2; // 0x84
	private float in_skill_time; // 0x88
	// Methods
	public void .ctor(); // 4c5200 - 18233
	private void Awake(); // 4c52b0 - 18234
	private void Start(); // 4c5398 - 18235
	private void Update(); // 4c53e8 - 18236
	private void FixedUpdate(); // 4c5534 - 18237
	public virtual void AutoLock(); // 4c5578 - 18238
	public virtual void SwitchWeapon(); // 4c5604 - 18239
	public virtual void RoleSkill(); // 4c5658 - 18240
	public virtual void RoleSkillEnd(); // 4c5a18 - 18241
	public virtual void RoleAtk(bool value); // 4c5bbc - 18242
	virtual void DeadChild(); // 4c5e80 - 18243
	public virtual void KillSomeOne(); // 4c5eb8 - 18244
	private void Hand2Atk(); // 4c5f00 - 18245
	private void Hand2AtkStop(); // 4c5f30 - 18246
	public virtual void Dizzy(float the_time); // 4c5f60 - 18247
}
每行代码后的注释是其在so里的偏移,把libil2cpp.so放到IDA中去分析,找到对应的ARM 汇编片段如下(Awake函数):

STMFD           SP!, {R4-R6,R10,R11,LR}
ADD             R11, SP, #0x10
MOV             R4, R0
LDR             R0, =(_GLOBAL_OFFSET_TABLE_ - 0x4C52CC)
LDR             R5, =0x44AE4
ADD             R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
ADD             R0, R5, R0
LDRB            R0, [R0,#(byte_D8E17D - 0xD8E0B0)]
CMP             R0, #0
BNE             loc_4C52FC
LDR             R0, =(_GLOBAL_OFFSET_TABLE_ - 0x4C52E8)
LDR             R1, =0x13FB8
ADD             R6, PC, R0 ; _GLOBAL_OFFSET_TABLE_
LDR             R0, [R1,R6]
LDR             R0, [R0]
BL              sub_AAB6B8
ADD             R0, R5, R6
MOV             R1, #1
到这里我已经一头雾水了——为了修改一个小东西还需要学整个ARM的指令集!不过要修改的东西应该是存档,既然存档保存在本地,应该有硬编码的存档路径字符串,于是乎又参考 这篇文章提取出了所有的字符串(包括方法名,类名,属性名),然而没法提取出代码内部的字符串,所以这条路暂时放弃了,等以后深入研究C++之后再来尝试。


二.尝试破解非il2cpp版本破解

去网上搜索了一下,发现已经有人做出了破解版本,下载解包后发现是旧版的游戏,没有采用il2cpp的方式进行编译,游戏运行启动时会弹出一个Toast广告,进入游戏后发现宝石特别多,其他逻辑都一样,猜测应该是修改了存档。既然启动时进行了弹窗,说明其修改了Android启动时的流程,这一点和Assembly-CSharp.dll无关,于是开始考虑安卓部分的反编译,找到了APKDB这个神器,解包classes.dex发现这段弹窗代码如下:

public class com
{
  public static void huluxia(Context paramContext)
  {
    String str1 = "" + "海量劲爆游戏请访问 ruansky.com" + "";
    String str2 = "" + "" + "";
    String str3 = "" + "" + "";
    String str4 = "" + "" + "";
    Toast.makeText(paramContext, Html.fromHtml(str1 + str2 + str3 + str4), 1).show();
    new StringBuilder(String.valueOf("海量劲爆游戏请访问 ruansky.com")).append("").append("").append("").toString();
  }
}
继续跟进调用者,最终入口处代码如下:

protected void onCreate(Bundle paramBundle)
{
    SavesRestoring.DoSmth(this);
    com.huluxia(this);
    com.huluxia(this);
    super.onCreate(paramBundle);
    // ...
}
这里的SaveRestoring.DoSmth(this)大概就是修改存档的地方,原来apk内有一个data.save的文件,这个文件其实是一个zip包,里面包含存档与设备信息等等,该函数在启动时会解压缩这个压缩文件覆盖掉PlayerPrefs生成的存档,达到修改的目的。存档文件是一个xml文件,内容如下:



    
    
    
    
    
    
    
    
    weapon_000
    null
    True
    
    True
    
    
    
    
    True
    
    
    
    True
    False
    
    True
    True
    712718
    a1da1c63918355a0c4dc69b79b865c6d
    
    
    
    
    False
    
    
    
    False
    
    
    
    5169003473483332933
    92f4c0361c1eb495db403ed6dc33f574
    
    
    False
    17-1-20%20%E4%B8%8B01%E6%97%B614%E5%88%8626%E7%A7%92
    True
    False
    
    1484890240049
    0%2C0%2C0%2C0%2C0
    
    
    
    
    False
    True
    false
    False
    True
    
    null
    
    
    
    
    
    
对比Assembly-CSharp.dll里的代码很容易就能确定Key-Value块代表的内容。经过一番修改这样就能解锁所有人物以及无限宝石之类Balabala....

但游戏里只有两条命,在不修改的人物属性的前提下想无限复活怎么办?只有修改Assembly-CSharp.dll里的逻辑了,下载ILSpy及插件Reflexil,找到RGController里的Reborn:

// RGController
public void Reborn()
{
	GameObject gameObject = UnityEngine.Object.Instantiate(Resources.Load("Effect/effect_reborn")) as GameObject;
	gameObject.transform.SetParent(base.transform, false);
	RGGameManager.GetInstance().ShowTextInfo(base.transform.position, ">_<", 3.5f, 1f);
	this.move_dir = Vector2.zero;
	this.awake = true;
	this.role_attribute.hp = this.role_attribute.max_hp;
	this.role_attribute.armor = this.role_attribute.max_armor;
	this.role_attribute.energy = this.role_attribute.max_energy;
	this.UpdateStateBar();
	RGGameProcess.GetInstance().has_reborn = true;
	base.transform.GetComponent().enabled = true;
	this.anim.SetTrigger("reborn");
	this.StartHitTrigger(2f);
}
将这里的
RGGameProcess.GetInstance().has_reborn = true;
修改为
RGGameProcess.GetInstance().has_reborn = false;
就可以了。修改后替换Dll,重新打包签名即可。

总结一点,il2cpp编译的Unity游戏修改需要的门槛比较高,常规的dll则修改起来比较容易,对那些dll加密可以参考这篇文章直接Dump内存中解密后的dll。

你可能感兴趣的:(逆向工程,Unity)