0x00 前言
使用riru v25
来写一个模块,点到即止。
关键词:Unity, libtersafe2, ObscuredFloat, Nullable<T>, arm64
0x01 思路一:借鉴ios jailBroken mod
从群友@black处得知国际服有一款适用于ios 64位设备的jailBroken Mod,功能大致如下:
Hack Features:
- Custom Defense
- No Skills Cooldown
- Custom Weapon Skill Damage
- Custom Critical Multiplier
- 100 Percent Critical Chance
- Custom Move Speed
- Custom Dash Speed
- God Mode For Campaign and Dungeon not for Arena or Colosseum
- Equipment Max Level
- Increased Equipment XP per item
- Unlock All Awakening Nodes and Activated
- Jailbreak Detection Bypassed
这里以Unlock All Awakeing Nodes and Activated
为例(解锁并点亮所有觉醒节点),看看这款Mod如何具体实现上述功能。
首先下载得到一个deb文件(deb,即 Debian包,是Unixar的标准归档,将包文件信息以及包内容,经过gzip和tar打包而成),使用7-zip
直接右键-提取文件,得到guardiantalesieauthmenu.dylib
。由于是内置菜单的mod,也许搜索一下字符串会有些线索,遂启动ida分析之,shift + F12打开Strings window
,搜索Awakening
:

查看引用,定位到sub_58D8+718
,F5查看伪代码:

v61 = switches;
*(_QWORD *)&v95[0] = 0x235ECCB99FA816D3LL;
std::string::string(&v91, "0x20008052C0035FD6", v90);
objc_msgSend(
v61,
"addOffsetSwitch:description:offsets:bytes:",
CFSTR("Unlocks All Awakening Nodes"),
CFSTR("Instant Cool"),
v95,
1LL,
&v91,
1LL);
联系上下文,猜测"0x235ECCB99FA816D3LL"
对应的是被hook函数的绝对地址,但这个值大的不正常,暂且先放一边。"0x20008052C0035FD6"
显而易见是一段Hex Code
,对应的Arm64 Assembly Code
是:
mov w0, #1
ret
即让被hook函数返回值"1"。
回过头接着分析前面提到的绝对地址。通过查看函数名,发现使用的是一款名为KittyMemory
的Hook框架(外国人用了都说好)。查看test/src/main.cpp
中关键代码:
// address = 0x6A6144
// bytes len = 8
// patch simple boolean return
gPatches.canShowInMinimap = MemoryPatch("libil2cpp.so", 0x6A6144, "\x01\x00\xA0\xE3\x1E\xFF\x2F\xE1", 8);
// by default MemoryPatch will cache library map for faster lookup when use getAbsoluteAddress
// You can disable this by passing false for last argument
//gPatches.canShowInMinimap = MemoryPatch("libil2cpp.so", 0x6A6144, "\x01\x00\xA0\xE3\x1E\xFF\x2F\xE1", 8, false);
// also possible with hex & no need to specify len
gPatches.canShowInMinimap = MemoryPatch::createWithHex("libil2cpp.so", 0x6A6144, "0100A0E31EFF2FE1");
// spaces are fine too
gPatches.canShowInMinimap = MemoryPatch::createWithHex("libil2cpp.so", 0x6A6144, "01 00 A0 E3 1E FF 2F E1");
这里我们围绕createWithHex
函数展开分析(为啥分析的不是MemoryPatch
?因为这个函数在dylib
里没有被引用呀_(:з」∠)_)。函数原型为 MemoryPatch::createWithHex(const char *libraryName, uintptr_t address, std::string hex, bool useMapCache)
,在dylib
中地址为0xD42C
。查看引用,定位到sub_4F40+7C
,直接查看伪代码:
v4 = objc_msgSend(menu, "getFrameworkName");
v5 = (a1 ^ 0x5751C3598CA4AA7FLL) - 0x740F0FDF0E1E31B4LL;
std::string::string((std::string *)&v13, a2);
MemoryPatch::createWithHex(v4, v5, &v13);
注意到变量v5
对应的正是函数原型中的address
,而v5
又是通过第二句中运算得出,看起来像是某种数值混淆的逆运算,猜测a1
就是前文中所提及的被混淆后的绝对地址。将a1 = 0x235ECCB99FA816D3LL
带入式中运算得出,此处v5 = 0x104EE8AF8
,对应游戏函数名为Oak.AwakeningTree$$IsNodeOpened
。
知道了函数名之后事情就变得简单了起来。在安卓版本的游戏中上述函数的对应地址为0x2167C1C
,参考@Perfare佬的使用Riru修改手游通过指针直接修改text段代码即可:
void *hack_thread(void *arg) {
LOGD("hack thread :%d", gettid());
unsigned long base_addr;
while (true) {
base_addr = get_module_base("libil2cpp.so");
if (base_addr != 0) {
break;
}
}
LOGD("detect libil2cpp.so %lx", base_addr);
LOGD("hack game begin");
unsigned long hack_addr = base_addr + 0x2167C1C;
//设置属性可写
void* page_start = (void*)(hack_addr - hack_addr % PAGE_SIZE);
if (-1 == mprotect(page_start, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC)) {
LOGE("mprotect failed(%d)", errno);
return NULL;
}
unsigned char* tmp1 = (unsigned char*)(void*)hack_addr;
tmp1[0] = 0x20;
tmp1[1] = 0x00;
tmp1[2] = 0x80;
tmp1[3] = 0x52;
unsigned char* tmp2 = (unsigned char*)(void*)(hack_addr + 0x4);
tmp2[0] = 0xC0;
tmp2[1] = 0x03;
tmp2[2] = 0x5F;
tmp2[3] = 0xD6;
LOGD("hack game finish");
return NULL;
}
Comments | 2 条评论
#该评论为私密评论#
淦吔, 后续呢