[GDTS]ver 2.5.5

发布于 2021-05-22  3.61k 次阅读


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查看伪代码:

sub_58D8+718
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;
}