Hook 与注入
- 软件加密技术不断更新迭代,攻防双方水平不断提升,单纯的静态级别的安全对抗已很少出现,分析人员面对得更多的是高强度的代码加密技术和程序防篡改技术,在此背景下,新的软件分析技术 —— Hook 与注入应运而生
- 反编译 APK、修改或添加代码后将 APK 重新打包,都会改变原文件的散列值和签名信息,软件防篡改技术即通过在软件运行时检查原文件的散列值和签名等手段判断程序是否遭到破坏。Hook 技术也叫“钩子技术”,原理是先将要修改的函数“钩住”,然后用自定义的函数将其替换,让程序在运行时执行自定义的函数,达到动态修改软件的目的。以 Hook 防篡改技术为例,防篡改系统在检测程序的散列值和签名时,会调用系统 API 读取 APK 签名信息,使用 Hook 技术,可“钩住”这些系统 API,直接返回原程序的签名信息,从而有效“欺骗”防篡改系统,解决代码重新打包后的签名检查问题。如此就涉及俩技术点:如何实施“钩住”这个动作;如何编写自定义的函数
Hook 的类型
- 按 Java 层和 Native 层分:
- Java 层的 Hook
- Native 层的 Hook
- (Java 层的 Hook)按代码运行环境分:
- Dalvik Hook
- ART Hook
- (Native 层的 Hook)按 ELF 文件的特点分:
- 基于动态库加载劫持的
LD_PRELOAD Hook
- 针对
.got.plt
节区的 GOT Hook - 针对汇编指令级别的 Inline Hook
- 基于动态库加载劫持的
Dalvik Hook
-
Dalvik 虚拟机运行的系统低于
Android 5.0
,早期安全研究人员对 Java 层的 Hook 记中在 Dalvik 虚拟机上。那个时期的 Hook 技术能实现,得益于:-
Java 语言的反射机制。对 Dalvik 虚拟机中任何一个 Java 类的方法进行 Hook,其本质即对 Java 语言中的 Method 类进行 Hook。通过 Java 的反射机制,DEX 文件中的代码很容易就能访问自己内存空间中的任何类的方法信息。在 Dalvik 虚拟机中,每个 Java 类的方法都对应
java/lang/reflect/Method
类 -
Dalvik 虚拟机底层的 Java 方法实现。在 Dalvik 虚拟机中,Java 方法的定义位于 Android 源码文件
dalvik/vm/oo/Object
中。Android 4.4
中其定义如下:struct method { ClassObject* clazz; u4 accessFlags; u2 methodIndex; u2 registersSize; u2 outsSize; u2 insSize; const char* name; DexProto Prototype; const char* shorty; /* the actual code */ const u2* insns; /* JNI: cached argument and return-type hints */ int jniArgInfo; DalvikBridgeFunc nativeFunc; bool fastJni; bool noRef; bool shouldTrace; const RegisterMap* registerMap; bool inProfile; };
- method 结构体中的字段完整地描述了一个 Java 方法地信息。可将它们的值修改为目标方法的相应字段,实现“移花接木”
-
可修改自身进程空间的内存。这既是 Linux 类操作系统进程的特点,即进程对自身内存空间的数据有绝对的控制权,也是各类 Android 热补丁插件得以实现的基石
-
-
有了这些条件,实现 Dalvik Hook 的思路就清晰了。ddi 工具的 Dalvik Hook 的核心实现代码:
void* dalvik_hook(struct dexstuff_t* dex, struct dalvik_hook_t* h) { if(h->debug_me) log("dalvik_hook: class %s\n", h->clname); void* target_cls = dex->dvmFindLoadedClass_fnPtr(h->clname); if(h->debug_me) log("class = 0x%x\n", target_cls); if(h->dump && dex && target_cls) dex->dvmDumpClass_fnPtr(target_cls, (void*)1); if(!target_cls) { if(h->debug_me) log("target_cls == 0\n"); return; } h->method = dex->dvmFindVirtualMethodHierByDescriptor_fnPtr(target_cls,h->method_name, h->method_sig); if(h->method == 0) { h->method = dex->dvmFindDirectMethodByDescriptor_dnPtr(target_cls, h->method_name, h->method_sig); } if(!h->resolvm) { h->cls = target_cls; h->mid = (void*)h->method; } if(h->debug_me) log("%s(%s) = 0x%x\n", h->method_name, h->method_sig, h->method); if(h->method