共计 5077 个字符,预计需要花费 13 分钟才能阅读完成。
Hook 技术须要事后剖析指标利用的源代码和逻辑,依据指标测试场景设置指标、逻辑和数据,而后运行时动静的对指标函数参数值、逻辑或者返回值做批改,达到批改现有函数逻辑、实现目标测试场景的目标。
Hook 的价值
在测试中,尽管通过批改数据以实现测试场景的需要,大部分状况下都能够通过 Mock 技术实现,然而还有一小部分场景,例如须要批改利用外部函数的参数、返回值或运行逻辑等状况,这时就须要用到 Hook 技术。
单元测试之外,Mock 技术的次要作用是对服务、接口进行 Mock,通过代理等形式将被测服务发送到依赖服务的申请转发给 Mock 服务,再由 Mock 服务依据规定组装预期的返回数据响应给被测服务,达到预期的测试场景。
Hook 技术次要用于服务外部代码逻辑上的批改,当函数间传递的参数或者函数内的逻辑须要进行批改时,数据的传递并没有通过网络,Mock 服务无奈对其进行操作,只能通过 Hook 技术通过在运行的代码中插入额定的代码或者在内存中进行操作。这种更精密更底层的批改,相比 Mock 技术能实现更多的批改范畴,适用性更广,难度也更大。
JVM Sandbox 简介
JVM-Sandbox 是 alibaba 开源的一个 JVM 沙箱容器,只能解决指标为 Java 利用的场景,次要的特点是反对热插拔(能够在指标利用运行中随时进行 Hook 的加载和解除)、能够同时操作挂载多个指标利用,相互之间独立设置互不烦扰、反对的指标利用 JDK 版本较广(6-11)。工具自身性能很多,在这里仅介绍和应用它用作 Hook 的局部性能。
JVM Sandbox 装置与启动
下载
我的项目的 github 地址:https://github.com/alibaba/jv…。
下载所需版本的二进制压缩包,解压(演示所应用的版本为 1.3.3)。
环境筹备
官网申明反对的零碎有:Linux/UNIX/MacOS,这几个零碎只须要下载解压缩就能够间接运行。
官网并未反对 Windows 零碎,所以须要进行如下批改:
装置 Git Bash。
装置 JDK(版本 6-11,演示所用版本为 1.8.0_192),门路中不能带有空格。
在 Shell 脚本中会有 Java 命令的调用,所以电脑中须要,并且因为 Git Bash 运行 Shell 脚本时的目录问题。
批改启动脚本 bin/sandbox.sh,将脚本中 183-188 行内容正文。
image
image
1080×277 95.5 KB
启动脚本
因为启动脚本中应用了相对路径,所以运行时须要切换到我的项目的 bin 目录下操作。
在 bin 目录中执行语句./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
JVM-Sandbox 同时还会对外提供接口,能够通过申请间接操作 JVM-Sandbox,这样就能不便的与本人的测试代码联合应用。
JVM Sandbox 示例
指标利用为一段简略的 Java 代码,代码中启动了一个死循环,每次循环会打印 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();
}
}
要编写合乎 JVM-Sandbox 的 hook 脚本,须要引入 sandbox-api 和 sandbox-debug-module 两个依赖。
通过实现 jvm.sandbox 中的 Module 接口,在 AdviceListener 办法中重写 before 办法,这样写入的语句就会在指标办法体执行之前进行执行,可能批改指标办法收到的参数数据。通过 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<String, String> 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);
}
});
}
}
我的项目通过 maven 治理依赖,对应的 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/m…d”>
<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>
脚本编写结束之后,将我的项目打成 Jar 包,放到下载的 JVM-Sandbox 我的项目下 sandbox-module 目录中。
image
启动写好的 java 目标程序,运行之后命令行开始循环打印之前设置好的语句,内容如下:
stringParam is a
boolParam is false
intParam is 666
stringParam is a
boolParam is false
intParam is 666
在 gitbash 命令行中关上 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
输入内容的变更,阐明 Hook 曾经失效。这样在指标程序运行中批改了办法传入的参数值,达到了 Hook 的目标。
当初执行语句./sandbox.sh -p 指标利用过程号 -S 能够敞开批改,命令行中输入的内容变回了原始的输入内容。
示例简略展现了 JVM-Sandbox 用作 Hook 工具的性能,通过 Hook 性能就能够对 Java 我的项目的外部运行逻辑和参数、返回值进行批改。测试场景的构建、测试用例的执行都变得更加不便哦~