前言
水一篇贴。声明:该帖用于技术交流分享,严禁恶意利用。
工具
必须:ida jadx frida il2cppdumper
前期分析

oppo壳加固,这里可以脱壳修改静态逻辑,但是为了图方便直接分析so动态hook
脱壳可参考这篇:https://www.52pojie.cn/thread-2049364-1-1.html
这里提取方式很多种,鄙人用adb pull
提取apk:adb pull /storage/emulated/0/NP/apks/2.apk ...(你的目录)
因为是unity,先提取相关资源分析

新手可以参考下述教程了解unity逆向(其实是鄙人懒狗):
https://xianyuuuan.space/2024/07/30/Unity%E5%87%BA%E9%A2%98-%E9%80%86%E5%90%91%E6%80%9D%E8%B7%AF%E7%AF%87/
https://mrwq.github.io/aggregate-paper/butian/%E6%B5%85%E8%B0%88CTF%E4%B8%AD%E7%9A%84unity%E6%B8%B8%E6%88%8F%E9%80%86%E5%90%91/
4.4.0新版本把global-metadata.dat加密了,lib文件太多没法定位加密,老版本可以直接dump
老规矩,丢ida修复符号表(分析Assembly-CSharp),然后就是结合各文件分析函数逻辑frida hook

分析&Hook
dump.cs是Unity游戏中IL2CPP方式打包后生成的文件,它包含了游>戏的类型、方法、字段等信息。
这里从dump.cs入手,ce修改unity也是同样思路,去找gamemanager
可以看到实例的创建以及gameinfo
// Namespace:
public class BearGameManager // TypeDefIndex: 5104
{
// Fields
private static BearGameManager instance; // 0x0
public BearGameInf mInf; // 0x10
// Methods
// RVA: 0x9AB138 Offset: 0x9AB138 VA: 0x9AB138
public void initialize(BearGameInf inf) { }
// RVA: 0x9AB140 Offset: 0x9AB140 VA: 0x9AB140
private void .ctor() { }
// RVA: 0x9AB148 Offset: 0x9AB148 VA: 0x9AB148
public static BearGameManager getInstance() { }
}
先修改无敌吧,info跟踪过去找到onhurt(其余方法同理)
这里因为是void方法可以hook后直接返回retn达到无伤效果
public class BearGameInfimpl : BearGameInf // TypeDefIndex: 5103
{
// ......
// RVA: 0x9A9BC4 Offset: 0x9A9BC4 VA: 0x9A9BC4 Slot: 52
public void OnHurt() { }
// ......
}
随后发现了 Jewel (珠宝),也就是我们最终的修改目标
// Namespace:
public class JewelManager : MonoBehaviour // TypeDefIndex: 5123
{
// Fields
public static JewelManager instance; // 0x0
public string ActivityID; // 0x18
public int MaxCoinCount; // 0x20
public int MinCoinCount; // 0x24
public int CoinCount; // 0x28
public int minPineAppleNew; // 0x2C
public int maxPineAppleNew; // 0x30
public int PineAppleCountNew; // 0x34
public int minStrawBerryNew; // 0x38
public int maxStrawBerryNew; // 0x3C
public int StrawBerryCountNew; // 0x40
public int minOrangeNew; // 0x44
public int maxOrangeNew; // 0x48
public int orangeCountNew; // 0x4C
public int minPineapple; // 0x50
public int maxPineapple; // 0x54
public int PineappleCount; // 0x58
public int minBanana; // 0x5C
public int maxBanana; // 0x60
public int BananaCount; // 0x64
public int minLollipop; // 0x68
public int maxLollipop; // 0x6C
public int LollipopCount; // 0x70
public int minRedEnvelope; // 0x74
public int maxRedEnvelope; // 0x78
public int RedEnvelopeCount; // 0x7C
public bool isOpenActivity; // 0x80
public bool isFromLackFragmentUI; // 0x81
// .....
}
中间的过程比较杂乱,记录下自己找到的关键类和方法
public class Item // TypeDefIndex: 4215
{
// Fields
private const string KEY_COUNT = "itemNum";
private const string KEY_LEVEL = "itemLevel";
public int itemId; // 0x10
public string name; // 0x18
public string spriteName; // 0x20
public int price; // 0x28
public string description; // 0x30
public string commond; // 0x38
public string tag; // 0x40
private string CountStr; // 0x48
public int quality; // 0x50
private string LevelStr; // 0x58
// Methods
// RVA: 0x1330870 Offset: 0x1330870 VA: 0x1330870
public void .ctor() { }
// RVA: 0x13308F8 Offset: 0x13308F8 VA: 0x13308F8
public void .ctor(int itemId) { }
// RVA: 0x1330994 Offset: 0x1330994 VA: 0x1330994 Slot: 4
public virtual void Init() { }
// RVA: 0x1330AC4 Offset: 0x1330AC4 VA: 0x1330AC4 Slot: 5
public virtual void InitOld() { }
// RVA: 0x132EE20 Offset: 0x132EE20 VA: 0x132EE20
public int get_Count() { }
// RVA: 0x132F090 Offset: 0x132F090 VA: 0x132F090
public void set_Count(int value) { }
// RVA: 0x1330BCC Offset: 0x1330BCC VA: 0x1330BCC
public int get_Level() { }
// RVA: 0x1330BE8 Offset: 0x1330BE8 VA: 0x1330BE8
public void set_Level(int value) { }
// RVA: 0x1330C1C Offset: 0x1330C1C VA: 0x1330C1C
public void Save() { }
}
接下来就是拷打氛围式编程了,通过frida动态hook
setImmediate(function () {
function hook() {
var base = Module.findBaseAddress("libil2cpp.so");
console.log(" libil2cpp base:", base);
function addr(rva){
return base.add(rva);
}
// =========================
// 无敌 OnHurt()
// RVA 0x9A9BC4
// =========================
Interceptor.replace(addr(0x9A9BC4), new NativeCallback(function(){
console.log("Block Hurt");
}, 'void', ['pointer']));
// =========================
// 无限复活 IsSurrection()
// RVA 0x9A93E8
// =========================
Interceptor.replace(addr(0x9A93E8), new NativeCallback(function(){
return 1;
}, 'bool', ['pointer']));
// =========================
// 不计死亡 AddDeadNum()
// RVA 0x9A93E4
// =========================
Interceptor.replace(addr(0x9A93E4), new NativeCallback(function(){
console.log("No death count");
}, 'void', ['pointer']));
// =========================
// 双倍金币
// RVA 0x9A8304
// =========================
Interceptor.replace(addr(0x9A8304), new NativeCallback(function(){
return 1;
}, 'bool', ['pointer']));
// =========================
// 游戏时间双倍金币
// RVA 0x9A842C
// =========================
Interceptor.replace(addr(0x9A842C), new NativeCallback(function(){
return 1;
}, 'bool', ['pointer']));
// =========================
// 全图吸铁
// RVA 0x9A8554
// =========================
Interceptor.replace(addr(0x9A8554), new NativeCallback(function(){
return 1;
}, 'bool', ['pointer']));
// =========================
// 道具时间 9999
// RVA 0x9A867C
// =========================
Interceptor.replace(addr(0x9A867C), new NativeCallback(function(){
return 9999;
}, 'float', ['pointer','int']));
// =========================
// Item.get_Count()
// RVA 0x132EE20
// =========================
Interceptor.attach(addr(0x132EE20), {
onLeave: function(retval){
retval.replace(9999);
}
});
// =========================
// Item.get_Level()
// RVA 0x1330BCC
// =========================
Interceptor.attach(addr(0x1330BCC), {
onLeave: function(retval){
retval.replace(99);
}
});
// =========================
// JewelManager.GetJewelValue()
// RVA 0x87391C
// =========================
Interceptor.attach(addr(0x87391C), {
onLeave: function(retval){
retval.replace(9999);
}
});
// =========================
// AddJewel() 掉落 x100
// RVA 0x873A04
// =========================
Interceptor.attach(addr(0x873A04), {
onEnter: function(args){
var value = args[2].toInt32();
args[2] = ptr(value * 100);
console.log("Jewel x100");
}
});
// =========================
// AddJewelInGame()
// RVA 0x873A7C
// =========================
Interceptor.attach(addr(0x873A7C), {
onEnter: function(args){
args[2] = ptr(999);
}
});
// =========================
// DeductJewelAndPearl()
// RVA 0x874774
// =========================
Interceptor.replace(addr(0x874774), new NativeCallback(function(){
console.log("No resource cost");
}, 'void', ['pointer','int']));
console.log(" Cheat Loaded");
}
function wait(){
var m = Process.findModuleByName("libil2cpp.so");
if(m){
hook();
}else{
setTimeout(wait,100);
}
}
wait();
});
frida -U -f com.xxx.xxx -l x.js
成果展示

广告去除还没考虑,可以进一步优化
题外话:第一次做春节论坛题目,学到了很多,也再次安利正己老师的安卓逆向课程