软件测试 | 测试开发 | 专项测试技术初识Hook

本文节选自霍格沃兹测试学院内部教材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 地址:github.com/alibaba/jvm...

下载所需版本的二进制压缩包,解压(演示所使用的版本为 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 行内容注释。​

启动脚本

由于启动脚本中使用了相对路径,所以运行时需要切换到项目的 bin 目录下操作。

在 bin 目录中执行语句./sandbox.sh -p 目标应用pid ,当出现如下提示信息,说明 JVM-Sandbox 已经成功启动了。

yaml 复制代码
                $ ./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 方法接收到的参数值,参数值已经在代码中固定传入,所以运行之后的结果是一串相同的输出内容。具体内容如下:

csharp 复制代码
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 开始,第二个参数为替换的值。具体代码如下:

java 复制代码
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 复制代码
    <?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>

脚本编写完毕之后,将项目打成 Jar 包,放到下载的 JVM-Sandbox 项目下 sandbox-module 目录中。

启动写好的 java 目标程序,运行之后命令行开始循环打印之前设置好的语句,内容如下:

csharp 复制代码
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 方法中传入的参数值,这时再回到目标程序运行的命令行中查看,可以看到命令行中输出的内容已经变更,如下

输出内容的变更,说明 Hook 已经生效。这样在目标程序运行中修改了方法传入的参数值,达到了 Hook 的目的。现在执行语句./sandbox.sh -p 目标应用进程号 -S 可以关闭修改,命令行中输出的内容变回了原始的输出内容。示例简单展示了 JVM-Sandbox 用作 Hook 工具的功能,通过 Hook 功能就可以对 Java 项目的内部运行逻辑和参数、返回值进行修改。测试场景的构建、测试用例的执行都变得更加方便

vbnet 复制代码
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 项目的内部运行逻辑和参数、返回值进行修改。测试场景的构建、测试用例的执行都变得更加方便

更多资料

相关推荐
大话性能4 小时前
【Pycharm 必学技巧 】以列为单位的块编辑
测试
蛋仔聊测试2 天前
Playwright 中route 方法模拟测试数据(Mocking)详解
前端·python·测试
loooooongger2 天前
(求虐)受不了前端天天改,自动化脚本天天崩!小弟肝了个工具,用“语义”干掉XPath,大佬们看这思路对吗?
低代码·测试
FIT2CLOUD飞致云3 天前
测试跟踪步骤描述用例交互优化,MeterSphere开源持续测试工具v2.10.26 LTS版本发布
软件测试·测试用例·测试·metersphere
霍格沃兹_测试4 天前
软件测试 | 测试开发 | 利用 pytest 玩转数据驱动测试框架
测试
Apifox6 天前
如何在 Apifox 中使用 OpenAPI 的 discriminator?
前端·后端·测试
utmhikari9 天前
【架构艺术】自动化测试平台架构设计的一些通用要点
自动化测试·功能测试·测试开发·架构·测试·系统设计·后端开发
漫谈测试10 天前
性能调优JVM参数优化方案有哪些
测试
研來如此10 天前
离线测试与在线测试
测试