2020-09-13

Cydia Substrate到Windows NT的简单移植

作者:好中文的样子 所属分类 - 安全 - 干货 - 黑科技

Cydia Substrate即Mobile Substrate,是一个曾经在iOS越狱插件上普遍使用的一键下钩子库。Cydia Substrate拥有一个非常简单的Hook接口,只需要C++工程中引入CydiaSubstrate的头文件即可简易使用该钩子库。Substrate技术广泛应用于Unix与类Unix平台上,例如安卓、iOS、Mac OS X以及普通Linux发行版都有广泛的使用Cydia Substrate。不仅如此,Cydia Substrate还支持X86与ARM指令集,并支持X64与ARM64,兼容性有保证。但是Cydia Substrate并没有适配Windows平台,而Windows平台虽然钩子库多,但是集成较差或者仅写死硬编码,不符合一套源码到处使用的原则。我们可以通过简易的移植,将Cydia Substrate移植到Windows Platform上。

Cydia Substrate

我们使用的编译器为MSVC,实际上使用Clang或者GCC+MinGW也是可以的,如果需要GNU的宏定义,可以使用Clang配合MSVC的库(VS2017工具集拥有Clang,无需手动各种Makefile)。

首先,我们把Cydia Substrate的库导入到Windows平台下的IDE当中,这里我们使用Visual Studio,只需要把Substrate的源码复制并导入进项目即可。我们很清楚的知道,Substrate的Hook导出函数是MSHookFunction,其函数定义看起来像是这样:

_extern void MSHookFunction(void *symbol, void *replace, void **result);

在Windows平台上,使用MSVC编译器,是不支持Linux或者GNU的宏的,因此我们在头文件当中,找到包含attribute的行,将带有attribute属性的每一行注释掉,并删除这些宏。

由于X86下MSVC编译器并没有自动链接__clear_cache这个函数,自带的MSVCRT库也没有这个函数的定义,因此我们可以删除调用这个函数的部分,这个函数是刷新处理器到内存之间的高速缓存区,也就是我们常说的L1、L2、L3缓存,这里我们可以不使用这个功能。

稍微了解Unix Posix接口以及Windows NT API就可以知道如何处理剩下的Compile Error,由于MSVC不支持C99的VLA,我们可以简单把栈换成C++的智能指针,使用上没有区别。这时候Compile出来库,直接程序内接入即可。

Visual Studio是傻瓜化操作,不需要加Makefile,我们只需要非常简单的将Substrate的所有cpp文件拖入源文件,就可以编译了。

为何Substrate直接移植到Windows上不会出任何问题?这个问题也是很简单,毕竟Windows目前只有x86和ARM的编译版本,PPC以及MIPS早已夭折,而Windows下函数调用实际上与Linux并无区别。例如,x86版本就是直接创建一块跳板,备份原函数头部的字节码,在跳板写入要执行的新函数、原函数头部备份以及要跳回的地址,在原函数头部写入跳转跳板的代码,修复原函数的所有字节码(例如跳转到函数头部Inline Evil OPCode段的jmp)。目前Substrate已闭源并商业化,最后2014的版本在ARM64支持上有问题,x86也没做函数重整,可以自己修复。

此外要注意一点,Substrate X86 Hook里面需要注释掉一段判断的内容,那个判断内容对于Windows来说并没有什么用处。

    if ((&*backup)[0] == 0xe9) {
        *result = reinterpret_cast<void *>(source + 5 + *reinterpret_cast<uint32_t *>((&*backup) + 1));
        MSLog(0,"0xe9");
        return;
    }
要注释的判断

为了避免Substrate写入的12字节超过原函数本身的大小,并覆盖后面的实际代码区,我们可以通过一些小方法减少写入的字节体积,例如本来12字节大小的Inline Payload,可以通过一个短跳优化成WORD,或者直接jmp大跳(5字节)到远程模块(DLL的)跳板再执行备份操作。对于ARM/ARM64,可以直接BX到跳板。

实现效果:Hook ZwTerminateProcess,我们打开ntdll.dll,再找到函数的地址,跟Linux上用法完全一样,见图。

使用Substrate Hook TerminateProcess

我们知道,调用系统API的TerminateProcess,最终都是走ntdll里面的Zw开头函数,因此hook掉ZwTerminateProcess就可以hook掉API导出函数,最后效果像是这样的:

Cydia Substrate 挂钩 Terminate

我们可以清楚看见,自己的函数已经执行。注意,部分__cdecl或者__fastcall的函数可能出现返回跳转崩溃的问题,跟寄存器状态恢复有关,不过测试众多函数都是没问题的。ARM版本的Windows使用x86程序并进行hook,可能出现一些不兼容的问题。执行Orig函数实际上就是跳到backup,backup里面的一些指令都是修复过的,放心使用。