CIBao 发表于 2019-6-22 08:31

一套强劲的 Il2cpp Hook 框架

本帖最后由 CIBao 于 2019-9-26 20:51 编辑

### 前言
前一段时间研究了一下Android的Native层Hook, 结果踩了一脚的雷
本文选用现成框架 **VirtualApp** 来实现Native层注入
对于il2cpp的Hook则采用的是替换类加载器进行针对性的HOOK
注意: **VirtualApp** 对安卓版本过高的系统不支持!

### 工具的准备
VirtualApp源码一份: (https://github.com/asLody/VirtualApp)
AndroidStudio和SDK: (https://developer.android.com/studio/index.html)
NDK低版本一份: (https://developer.android.com/ndk/downloads)

以上工具获取后请先自行编译vapp的源码, 能够正常编译出签名版本即可
### 通过vapp的接口进行拓展
#### 定位到目标接口
定位到 VirtualApp/lib/src/main/jni/Foundation/IOUniformer.cpp 文件
vapp劫持了所有内部软件的dlopen调用, 留下了一个接口 `onSoLoaded` 以供开发人员进行其他操作

#### 过滤无效的文件
`onSoLoaded` 的第一个形参是**文件路径**, 第二个形参是**句柄**
只需要判断 **文件路径** 是否包含我们需要的lib就好了
```cpp
if (!strstr(name, "libil2cpp.so"))
    return;
```

#### 通过dlsym定位il2cpp的内置函数
查看Unity项目源码中Class的定义, 会发现几个有意思的函数
```csharp
namespace il2cpp {
namespace vm{
class Class{
    ...
    static TypeInfo* FromName (Il2CppImage* image, const char* namespaze, const char *name);
    static const MethodInfo* GetMethodFromName (TypeInfo *klass, const char* name, int argsCount);
    static const MethodInfo* GetMethodFromNameFlags (TypeInfo *klass, const char* name, int argsCount, int32_t flags);
    ...
};
} /* namespace vm */
} /* namespace il2cpp */
```
`FromName` 函数是用来获取类的镜像 ~~(字节码文件?没学过C#蛋疼T T)~~
`GetMethodFromName` 则是用来获取指定的方法地址
可以直接通过 `onSoLoaded` 接口传入的 **句柄** 定位这两个函数
```cpp
void *get_method_addr = dlsym(handle, "il2cpp_class_get_method_from_name");
void *get_class_addr = dlsym(handle, "il2cpp_class_from_name");
```
> GetMethodFromName方法实际上会去调用GetMethodFromNameFlags
> 而flags参数是用来区分函数重载的各个函数, 默认为0

#### 处理偏移
由于是导出函数, 编译器都会进行优化, 都是抬手就 **B指令**
导致没法使用Inline Hook, 想要使用Inline Hook去Hook函数, 这个函数就必须拥有两条指令以上才能Hook成功

---
**IDA中的il2cpp_class_get_method_from_name**
!(https://mihoyo.top/wp-content/uploads/il2cpp-hook-IDAFunc_il2cpp_class_get_method_from_name.png)
!(https://mihoyo.top/wp-content/uploads/il2cpp-hook-IDAFunc_il2cpp_class_get_method_from_name_0.png)

---
**IDA中的il2cpp_class_from_name**
!(https://mihoyo.top/wp-content/uploads/il2cpp-hook-IDAFunc_il2cpp_class_from_name.png)
!(https://mihoyo.top/wp-content/uploads/il2cpp-hook-IDAFunc_il2cpp_class_from_name_0.png)
!(https://mihoyo.top/wp-content/uploads/il2cpp-hook-IDAFunc_sub_5F41AC8.png)

---
通过手动解析 **B指令** 地址, 获取到我们Hook的理想位置
```cpp
inline void* CorrectBTarget(void *symbol){
    return ((char*)symbol + (*(int32_t*)(symbol) << 8 >> 6) + 8);
}
```
1. `*(int32_t *)(symbol)` 获取内存中指令的HEX
2. `<< 8` 抹除**B指令**的HEX, 但是内存中的HEX跟IDA中看到HEX是倒过来的, 所以不能用右移
3. `>> 6` 初步解析指令的偏移
4. `+ 8` 人工计算具体偏移. 初步的解析指令偏移并不能一次性到点, 还是得靠人工修正一下
5. `(char*)symbol +` 初始的地址加上偏移的地址就是目标的地址

若不止跳转一次, 按照上面的步骤继续解析加人工偏移就好了
```cpp
inline void* CorrectGetMethodAddr(void *symbol) {
    return ((char*)symbol + (*(int32_t*)(symbol) << 8 >> 6) + (*(int32_t*)((char*)symbol + (*(int32_t*)(symbol) << 8 >> 6) + 12) << 8 >> 6) + 20);
}
inline void* CorrectGetClassAddr(void *symbol) {
    return ((char*)symbol + (*(int32_t*)(symbol) << 8 >> 6) + (*(int32_t *)((char*)symbol + (*(int32_t *)(symbol) << 8 >> 6) + 8) << 8 >> 6) + 16);
}
```

#### 开始Hook
创建要Hook的函数原型, 还有要替换进去的新函数
要替换进去的函数的参数必须跟原函数一样, 否则会替换失败
那些特殊的 **类指针/结构体指针/等等** 直接写 `void*` 就好了
```cpp
void *(*_Il2cpp_ClassGetMethod)(void *, const char *, int, int32_t);
void *(*_Il2cpp_GetClass)(void *, const char *, const char *);

void *Il2cpp_ClassGetMethod(void *klass, const char *name, int argsCount, int32_t flags)
{
    return _Il2cpp_ClassGetMethod(klass, name, argsCount, flags);
}

void *Il2cpp_GetClass(void *image, const char *namespaze, const char *name)
{
    //LOGE("-> %s -> %s", namespaze, name);
    return _Il2cpp_GetClass(image, namespaze, name);
}
```

写好函数原型后就可以进行HOOK了, 直接使用vapp集成好的 `MSHookFunction` 直接HOOK就完事
```cpp
//GetMethodFromName
MSHookFunction(CorrectGetMethodAddr(get_method_addr),
                           (void*)Il2cpp_ClassGetMethod,
                           (void**)&_Il2cpp_ClassGetMethod);
//GetClassFromName
MSHookFunction(CorrectGetClassAddr(get_class_addr),
                           (void*)Il2cpp_GetClass,
                           (void**)&_Il2cpp_GetClass);
```
正常启动一遍vapp, 将目标应用安装进去后启动, 查看log输出 ~~(这里偷懒就莫得图咯)~~

#### 修改替换进去的函数实现针对性Hook
可以选择判断 **名称空间** 或者 **类名** 进行下一步操作
因为后面还有Hook, 不能重复执行, 来一个全局变量限制一下就好
```cpp
bool (*_AvatarManager_GetIsAutoBattle)(void*);
bool AvatarManager_GetIsAutoBattle(void* self) {return true;}

bool hookLock = false;
void* Il2cpp_GetClass(void* image, const char* namespaze, const char* name) {
    void* result = _Il2cpp_GetClass(image, namespaze, name);
    //限制Hook
    if (isHookAgain || strcmp(namespaze, "MoleMole"))
      return result;
    isHookAgain = true;

    //通过具体的 名称空间 还有 类名, 获取到对应的类
    void* AvatarManager = Il2cpp_GetClass(image, "MoleMole", "AvatarManager");
    //通过指定的类去Hook指定的方法
    hook_Il2cppFunc(AvatarManager, "get_isAutoBattle", 0,
                                        AvatarManager_IsAutoBattle, &_AvatarManager_IsAutoBattle);

    return result;
}
```
注意一下: 由于C#是面向对象的语言, 普通成员函数**第一个参数始终是实例自身**, 也就是this, C#里写代码的时候并不需要自己加上, 但是Hook的函数原型必须加上, 否则会出大问题

`hook_Il2cppFunc` 函数对GetMethod的方法进行了封装, 通过 **类的image** 和 **成员方法名**, 即可获取到对应的函数地址
```cpp
template <class t1, class t2>
void hook_Il2cppFunc(void *classImage, const char *funcName, int argsCount, t1 newFunc, t2 origFunc)
{
    if (classImage)
    {
      int32_t *addr = (int32_t *)Il2cpp_ClassGetMethod(classImage, funcName, argsCount, 0);
      if (addr)
      {
            MSHookFunction(*addr, (void*)newFunc, (void**)origFunc);
            LOGD("HOOK SUCCESS >>> [%s]", funcName);
      }
      else
            LOGD("HOOK ERROR!! >>> [%s] ", funcName);
    }
}
```
`argsCount` 是函数原型的参数个数, 无需将this算入其内

想要Hook其他方法, 只需要写上对应的 **函数原型**、**要Hook进去的新函数**、**函数原型参数个数**、**具体的类image**, 并在 `Il2cpp_GetClass` 方法里面进行Hook, 就ok辣

### 总结
这套方案只要实现成功后基本上不需要过多的维护
用了一段时间了, 缺点总结两点
1. 遇到人工混淆的函数就嗝屁了
2. 内部类目前还未找到方法实现抓取(螺的岛就用到了大量的内部类)
~~(之前的文章由于媒体库炸了, 图片全丢失, 所以重新优化了一遍)~~

#### 相关 教程/工具
(https://www.52pojie.cn/thread-618515-1-1.html)
(https://www.perfare.net/tag/il2cppdumper)
[将注入进行到底:利用Mono注入C#游戏脚本](http://blog.sycsec.com/2019/01/07/Injecting-code-into-C-Sharp-game-scripts-via-Mono/?tdsourcetag=s_pcqq_aiomsg)

740002600 发表于 2019-9-29 20:16

大神,求教育, 下面的地址 怎么计算出来呀?
.text:0061823C F0 48 2D E9                                 STMFD         SP!, {R4-R7,R11,LR}
.text:00618240 10 B0 8D E2                                 ADD             R11, SP, #0x10
.text:00618244 08 D0 4D E2                                 SUB             SP, SP, #8
.text:00618248 00 40 A0 E1                                 MOV             R4, R0
.text:0061824C 0C 01 9F E5                                 LDR             R0, =(off_2A57B2C - 0x61825C)
.text:00618250 01 50 A0 E1                                 MOV             R5, R1
.text:00618254 00 00 9F E7                                 LDR             R0, ; dword_2A9B364
.text:00618258 0C 00 80 E2                                 ADD             R0, R0, #0xC
.text:0061825C 00 00 84 E5                                 STR             R0,
.text:00618260 08 60 95 E5                                 LDR             R6,
.text:00618264 06 00 A0 E1                                 MOV             R0, R6; s
.text:00618268 C5 FA F4 EB                                 BL            strlen
.text:0061826C 00 20 A0 E1                                 MOV             R2, R0
.text:00618270 04 00 A0 E1                                 MOV             R0, R4
.text:00618274 06 10 A0 E1                                 MOV             R1, R6
.text:00618278 9E A9 00 FA                                 BLX             sub_6428F8
.text:0061827C 2B 00 D5 E5                                 LDRB            R0,
.text:00618280 02 00 10 E3                                 TST             R0, #2
.text:00618284 24 00 00 0A                                 BEQ             loc_61831C
.text:00618288 1C 00 95 E5                                 LDR             R0,
.text:0061828C 08 60 90 E5                                 LDR             R6,
.text:00618290 00 00 56 E3                                 CMP             R6, #0
.text:00618294 20 00 00 0A                                 BEQ             loc_61831C

pp7560 发表于 2019-9-30 19:21

大神,不会算,算了半天一直错的,不知道怎么求出il2cpp_class_get_method_from_name正确地址的,这是的so 跳转2次后的相关信息

.text:01A11998                     EXPORT il2cpp_class_get_method_from_name
.text:01A11998   il2cpp_class_get_method_from_name       ; DATA XREF: LOAD:0019BB18↑o
.text:01A11998 000               B               j__ZN6il2cpp2vm5Class17GetMethodFromNameEP11Il2CppClassPKci ;// plt loc_BFC8D0 il2cpp::vm::Class::GetMethodFromName(Il2CppClass *,char const*,int)
.text:01A11998   ; End of function il2cpp_class_get_method_from_name

loc_BFC8D0


.text:019EA184 ; il2cpp::vm::Class::GetMethodFromName(Il2CppClass *, char const*, int)
.text:019EA184               EXPORT _ZN6il2cpp2vm5Class17GetMethodFromNameEP11Il2CppClassPKci
.text:019EA184 _ZN6il2cpp2vm5Class17GetMethodFromNameEP11Il2CppClassPKci
.text:019EA184                                       ; CODE XREF: il2cpp::vm::Class::GetMethodFromName(Il2CppClass *,char const*,int)+8↑j
.text:019EA184                                       ; DATA XREF: LOAD:002DD1C8↑o
.text:019EA184 ; __unwind {
.text:019EA184               MOV             R3, #0
.text:019EA188               B               j__ZN6il2cpp2vm5Class22GetMethodFromNameFlagsEP11Il2CppClassPKcii ; il2cpp::vm::Class::GetMethodFromNameFlags(Il2CppClass *,char const*,int,int)
.text:019EA188 ; } // starts at 19EA184
.text:019EA188 ; End of function il2cpp::vm::Class::GetMethodFromName(Il2CppClass *,char const*,int)
.text:019EA188
.text:019EA18C

.text:019EA18C ; Attributes: bp-based frame
.text:019EA18C
.text:019EA18C ; il2cpp::vm::Class::GetMethodFromNameFlags(Il2CppClass *, char const*, int, int)
.text:019EA18C               EXPORT _ZN6il2cpp2vm5Class22GetMethodFromNameFlagsEP11Il2CppClassPKcii
.text:019EA18C _ZN6il2cpp2vm5Class22GetMethodFromNameFlagsEP11Il2CppClassPKcii
.text:019EA18C                                       ; CODE XREF: il2cpp::vm::Class::GetMethodFromNameFlags(Il2CppClass *,char const*,int,int)+8↑j
.text:019EA18C                                       ; DATA XREF: LOAD:0013B588↑o
.text:019EA18C ; __unwind {
.text:019EA18C               STMFD         SP!, {R4-R11,LR}    //关键点应该是这里
.text:019EA190               ADD             R11, SP, #0x1C
.text:019EA194               SUB             SP, SP, #4
.text:019EA198               MOV             R8, R3
.text:019EA19C               MOV             R9, R2
.text:019EA1A0               MOV             R6, R1
.text:019EA1A4               MOV             R7, R0
.text:019EA1A8               BL            j__ZN6il2cpp2vm5Class4InitEP11Il2CppClass ; il2cpp::vm::Class::Init(Il2CppClass *)
.text:019EA1AC               MOV             R5, #0
.text:019EA1B0               CMP             R7, #0
.text:019EA1B4               BEQ             loc_19EA234
.text:019EA1B8               MOV             R10, #0
.text:019EA1BC               MOV             R4, SP

Light紫星 发表于 2019-6-22 09:00

感谢楼主分享。

mancong122 发表于 2019-6-22 09:43

小老弟很有想法,遇到加壳的直接hook脱壳解密入口又能拓展出脱壳解密功能{:301_997:}

呱某包子 发表于 2019-6-22 10:14

腻害腻害

多幸运遇见baby 发表于 2019-6-22 10:37

谢谢@Thanks!

胖子哦 发表于 2019-6-22 11:19


谢谢@Thanks!

syrmb 发表于 2019-6-22 12:08

{:1_918:}神之GayHub

花颜 发表于 2019-6-22 12:19

真的厉害~!!

qhr319 发表于 2019-6-22 12:21

厉害支持

lllcrhlll 发表于 2019-6-22 12:52

感谢分享
页: [1] 2 3 4 5
查看完整版本: 一套强劲的 Il2cpp Hook 框架