本文节选自霍格沃兹测试学院内部教材
Hook 技术需要预先分析目标应用的源代码和逻辑,根据目标测试场景设置目标、逻辑和数据,然后运行时动态的对目标函数参数值、逻辑或者返回值做修改,达到修改现有函数逻辑、实现目标测试场景的目的。
在测试中,虽然通过修改数据以实现测试场景的需求,大部分情况下都可以通过 Mock 技术实现,但是还有一小部分场景,例如需要修改应用内部函数的参数、返回值或运行逻辑等情况,这时就需要用到 Hook 技术。
单元测试之外,Mock 技术的主要作用是对服务、接口进行 Mock,通过代理等方式将被测服务发送到依赖服务的请求转发给 Mock 服务,再由 Mock 服务根据规则组装预期的返回数据响应给被测服务,达到预期的测试场景。
Hook 技术主要用于服务内部代码逻辑上的修改,当函数间传递的参数或者函数内的逻辑需要进行修改时,数据的传递并没有经过网络,Mock 服务无法对其进行操作,只能通过 Hook 技术通过在运行的代码中插入额外的代码或者在内存中进行操作。这种更精细更底层的修改,相比 Mock 技术能实现更多的修改范围,适用性更广,难度也更大。

JVM Sandbox简介
JVM-Sandbox 是 alibaba 开源的一个 JVM 沙箱容器,只能处理目标为 Java 应用的场景,主要的特点是支持热插拔(可以在目标应用运行中随时进行 Hook 的加载和解除)、可以同时操作挂载多个目标应用,相互之间独立设置互不干扰、支持的目标应用 JDK 版本较广(6-11)。工具本身功能很多,在这里仅介绍和使用它用作 Hook 的部分功能。
bin/sandbox.sh ,将脚本中 183-188 行内容注释。
./sandbox.sh -p 目标应用pid ,当出现如下提示信息,说明 JVM-Sandbox 已经成功启动了。- $ ./sandbox.sh -p 6204
-
- NAMESPACE : default
-
- VERSION : 1.3.3
-
- MODE : ATTACH
-
- SERVER_ADDR : 0.0.0.0
-
- SERVER_PORT : 4543
-
- UNSAFE_SUPPORT : ENABLE
-
- SANDBOX_HOME : e:/Download/sandbox/bin/..
-
- SYSTEM_MODULE_LIB : e:/Download/sandbox/bin/..\module
-
- USER_MODULE_LIB : E:\Download\sandbox\sandbox-module;~/.sandbox-module;
-
- SYSTEM_PROVIDER_LIB : e:/Download/sandbox/bin/..\provider
-
- EVENT_POOL_SUPPORT : DISABLE
report 方法接收到的参数值,参数值已经在代码中固定传入,所以运行之后的结果是一串相同的输出内容。具体内容如下:- public class HookTarget {
-
- final void report(String stringParam, boolean boolParam, int intParam) {
-
- System.out.println("stringParam is " + stringParam);
-
- if (boolParam) {
-
- System.out.println("boolParam is true!");
-
- } else {
-
- System.out.println("boolParam is false");
-
- }
-
- System.out.println("intParam is " + intParam);
-
- }
-
- final void loopReport() throws InterruptedException {
-
- while (true) {
-
- report("a", false, 666);
-
- Thread.sleep(1000);
-
- System.out.println();
-
- }
-
- }
-
- public static void main(String... args) throws InterruptedException {
-
- new HookTarget().loopReport();
-
- }
-
- }
sandbox-api和sandbox-debug-module 两个依赖。advice.changeParameter 方法,修改对应位置的参数数值,第一个参数为目标参数的位置,从 0 开始,第二个参数为替换的值。具体代码如下:- import com.alibaba.jvm.sandbox.api.Information;
- import com.alibaba.jvm.sandbox.api.Module;
- import com.alibaba.jvm.sandbox.api.annotation.Command;
- import com.alibaba.jvm.sandbox.api.listener.ext.Advice;
- import com.alibaba.jvm.sandbox.api.listener.ext.AdviceListener;
- import com.alibaba.jvm.sandbox.api.listener.ext.EventWatchBuilder;
- import com.alibaba.jvm.sandbox.api.resource.ModuleEventWatcher;
- import org.kohsuke.MetaInfServices;
- import javax.annotation.Resource;
- import java.util.*;
-
- @MetaInfServices(Module.class)
- @Information(id = "ceshiren.com", author = "ceshiren.com")
- public class hook_jvm implements Module {
-
- @Resource
-
- private ModuleEventWatcher moduleEventWatcher;
-
- @Command("ceshiren")
-
- public void ceshiren(final Map
param) { -
- new EventWatchBuilder(moduleEventWatcher)
-
- .onClass("HookTarget")
-
- .onBehavior("report")
-
- .onWatch(new AdviceListener() {
-
- @Override
-
- protected void before(Advice advice) throws Throwable {
-
- advice.changeParameter(0, "Change By Hook!");
-
- advice.changeParameter(1, false);
-
- advice.changeParameter(2, 965);
-
- }
-
- });
-
- }
-
- }
pom.xml 文件内容如下:- <?xml version="1.0" encoding="UTF-8"?>
- <project xmlns="http://maven.apache.org/POM/4.0.0"
-
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
- <modelVersion>4.0.0</modelVersion>
-
- <groupId>org.example</groupId>
-
- <artifactId>ceshiren_book</artifactId>
-
- <version>1.0-SNAPSHOT</version>
-
- <dependencies>
-
- <dependency>
-
- <groupId>com.alibaba.jvm.sandbox</groupId>
-
- <artifactId>sandbox-api</artifactId>
-
- <version>1.3.3</version>
-
- </dependency>
-
- <dependency>
-
- <groupId>com.alibaba.jvm.sandbox</groupId>
-
- <artifactId>sandbox-debug-module</artifactId>
-
- <version>1.3.3</version>
-
- <scope>compile</scope>
-
- </dependency>
-
- </dependencies>
-
- <build>
-
- <plugins>
-
- <plugin>
-
- <groupId>org.apache.maven.plugins</groupId>
-
- <artifactId>maven-assembly-plugin</artifactId>
-
- <executions>
-
- <execution>
-
- <goals>
-
- <goal>attached</goal>
-
- </goals>
-
- <phase>package</phase>
-
- <configuration>
-
- <descriptorRefs>
-
- <descriptorRef>jar-with-dependencies</descriptorRef>
-
- </descriptorRefs>
-
- </configuration>
-
- </execution>
-
- </executions>
-
- </plugin>
-
- </plugins>
-
- </build>
-
- </project>

- stringParam is a
-
- boolParam is false
-
- intParam is 666
-
- stringParam is a
-
- boolParam is false
-
- intParam is 666
sandbox/bin 目录,执行语句./sandbox.sh -p目标应用进程号-d 'ceshiren.com/ceshiren' ,启动 JVM-Sandbox 并对目标程序进行 Hook 操作,变更report 方法中传入的参数值,这时再回到目标程序运行的命令行中查看,可以看到命令行中输出的内容已经变更,如下:- stringParam is Change By Hook!
-
- boolParam is false
-
- intParam is 965
-
- stringParam is Change By Hook!
-
- boolParam is false
-
- intParam is 965
./sandbox.sh -p 目标应用进程号 -S 可以关闭修改,命令行中输出的内容变回了原始的输出内容。示例简单展示了 JVM-Sandbox 用作 Hook 工具的功能,通过 Hook 功能就可以对 Java 项目的内部运行逻辑和参数、返回值进行修改。测试场景的构建、测试用例的执行都变得更加方便哦~
