Skip to content

Commit

Permalink
2.3 add plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
keven1z committed Jun 26, 2022
1 parent 591d364 commit 7a02b81
Show file tree
Hide file tree
Showing 15 changed files with 104 additions and 68 deletions.
48 changes: 32 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# DHook ![2.2.1 (shields.io)](https://img.shields.io/badge/2.2.1-brightgreen.svg)
# DHook ![2.3 (shields.io)](https://img.shields.io/badge/2.3-brightgreen.svg)
DHook是一个交互式自定义动态hook的工具。通过`javaagent`+`ASM`技术对运行时的java应用进行字节码修改,并可以配置文件的方式来增加hook点,修改执行方法的返回值以及参数等。

## 兼容性
* java 8-11
## 环境
* jdk 8-11

## 快速开始

Expand All @@ -18,7 +18,7 @@ DHook是一个交互式自定义动态hook的工具。通过`javaagent`+`ASM`技
| | \ :| .--. || | | || | | || . '
| '--' /| | | |' '-' '' '-' '| |\ \
`-------' `--' `--' `-----' `-----' `--' '--'
:: DHook ::2.0 springboot: (v2.6.2)
:: DHook ::2.3 springboot: (v2.6.2)
2022-02-13 15:17:03.856 INFO 12496 --- [ main] com.keven1z.DHookServerApplication : Starting DHookServerApplication v2.0 using Java 1.8.0_171 on zii with PID 12496
2022-02-13 15:17:03.856 INFO 12496 --- [ main] com.keven1z.DHookServerApplication : No active profile set, falling back to default profiles: default
Expand Down Expand Up @@ -87,6 +87,10 @@ agent所捕获的Hook的所有类名
#### 导出agent
将会导出包含hook点的agent,该agent不与服务端绑定,去除了多余的api调用,仅作用hook点的修改,体积很小。

### 插件
> 插件编写见[plugin](./plugin.md)
插件与agent id绑定,进入对应的agent的导入插件,插件放在plugins文件夹中。

### 案例

Expand All @@ -106,27 +110,39 @@ agent所捕获的Hook的所有类名


## 更新
### 1.0版本
* 支持hook接口,当填写的类为接口时,默认会hook所有实现的子类
* 支持更改hook类的返回类型为string,int,boolean的返回值
* 支持打印hook方法的所有参数值
### 2.3版本 2022/6/26

* 增加插件功能

### 2.2版本

* 增加导出仅包含hook点信息的agent

### 2.1版本

* 可以增加方法执行前后静态方法执行参数
* 增加方法执行前后,直接return

### 2.0版本

* 增加交互式的hook操作
* 增加方法执行前后的修改

### 1.1版本

* 增加通过`*`打印该类的所有方法
* 支持打印返回值
* 支持反编译代码
* 支持修改参数

### 2.0版本
### 1.0版本
* 支持hook接口,当填写的类为接口时,默认会hook所有实现的子类
* 支持更改hook类的返回类型为string,int,boolean的返回值
* 支持打印hook方法的所有参数值



* 增加交互式的hook操作
* 增加方法执行前后的修改

### 2.1版本

* 可以增加方法执行前后静态方法执行参数
* 增加方法执行前后,直接return

### 2.2版本

* 增加导出仅包含hook点信息的agent
Binary file added dHook.zip
Binary file not shown.
Binary file modified dhook.sqlite
Binary file not shown.
62 changes: 62 additions & 0 deletions plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# 编写你的插件
## API
1. IDHookExtender: DHook抽象类,包含onMethodExit,onMethodEnter,onVisitCode,修改方法不同调用时机的抽象方法
2. IDHookExtenderCallbacks:DHook抽象类帮助类,主要负责设置插件名,hook点等信息

## 编写插件
1. 下载[dHook.zip](./dHook.zip)
2. 解压dHook.zip,放入你的插件项目中。
3. 编写名为`DHookExtender`的类继承IDHookExtender,实现对应的抽象方法
4. 在registerExtenderCallbacks中通过callbacks对象设置hook点等信息

## 案例
```java
public class DHookExtender extends IDHookExtender {
public void registerExtenderCallbacks(IDHookExtenderCallbacks idHookExtenderCallbacks) {
ArrayList<String> list = new ArrayList<>();
list.add("java.sql.Statement.executeQuery(Ljava/lang/String;)Ljava/sql/ResultSet;");
idHookExtenderCallbacks.setExtensionHooks(list);
idHookExtenderCallbacks.setExtensionName("sql语句打印插件");
idHookExtenderCallbacks.setExtensionDesc("该工具可以打印所有应用执行的语句");
}

public void onMethodExit(AdviceAdapter adviceAdapter, int opcode) {
if (opcode == 177) {
adviceAdapter.visitInsn(1);
} else if (opcode == 176 || opcode == 191) {
adviceAdapter.dup();
} else {
if (opcode == 173 || opcode == 175) {
adviceAdapter.dup2();
} else {
adviceAdapter.dup();
}
adviceAdapter.box(Type.getReturnType(getDesc()));
}
Type type = Type.getType(DHookExtender.class);
Method method = new Method("doHook", "(Ljava/lang/Object;[Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
adviceAdapter.loadArgArray();
adviceAdapter.visitLdcInsn(getClassName());
adviceAdapter.visitLdcInsn(getMethod());
adviceAdapter.visitLdcInsn(getDesc());
adviceAdapter.invokeStatic(type, method);
}

public void onMethodEnter(AdviceAdapter adviceAdapter) {}

public void onVisitCode(AdviceAdapter adviceAdapter) {}

public static void doHook(Object returnValue, Object[] args, String className, String method, String desc) {
StringBuilder sb = new StringBuilder();
for (Object arg : args)
sb.append(arg).append("\t");
String argStr = sb.toString();
System.out.println(className + "." + method + desc + "\n\t"+ argStr);
if (returnValue != null)
System.out.println("\t"+ returnValue);
}
}
```
> jdk9+ 编写插件时,tomcat应用需要修改catalina.sh以下选项
> JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/jdk.internal.loader=ALL-UNNAMED"
> JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=jdk.zipfs/jdk.nio.zipfs=ALL-UNNAMED"
Binary file added plugins/SqlPrinter.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</parent>
<groupId>com.keven1z</groupId>
<artifactId>DHookServer</artifactId>
<version>2.2</version>
<version>2.3</version>
<name>DHookServer</name>
<description>DHookServer</description>
<properties>
Expand Down
4 changes: 0 additions & 4 deletions src/main/java/com/keven1z/DHookServerApplication.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.keven1z;

import com.keven1z.utils.PluginUtil;
import io.netty.channel.ChannelFuture;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
Expand All @@ -17,7 +16,6 @@
@MapperScan(basePackages = {"com.keven1z.dao"})
public class DHookServerApplication implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);
private static final PluginUtil pluginUtil = new PluginUtil();
@Resource
NettyServer nettyServer;
public static void main(String[] args) {
Expand All @@ -28,8 +26,6 @@ public void run(String... args) throws InterruptedException, IOException, ClassN
// 开启服务
int port = 7070;
ChannelFuture future = nettyServer.start("localhost", port);
/* 加载插件*/
// pluginUtil.initPlugins();
if (future.isSuccess()){
logger.info("Netty started on port(s): "+port);
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/keven1z/controller/AgentController.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public int add(@RequestParam String name){

/**
* 导出agent
* @param id dHook.jar id
* @param id dHook.jar1 id
* @return
*/
@GetMapping("/export")
Expand Down
39 changes: 2 additions & 37 deletions src/main/java/com/keven1z/controller/HookController.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,16 @@
import com.keven1z.utils.GsonUtil;
import com.keven1z.utils.HttpUtil;
import com.keven1z.utils.JarUtil;
import com.keven1z.utils.PluginUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
* @author keven1z
Expand Down Expand Up @@ -50,7 +44,7 @@ public List<HookEntity> allHook() {
/**
* 通过agent id查找hook数据
*
* @param id dHook.jar id
* @param id dHook.jar1 id
* @return hook的json格式数据
*/
@GetMapping("/find")
Expand All @@ -72,7 +66,7 @@ public int del(String hookId) {
/**
* 导出配置文件
*
* @param id dHook.jar id
* @param id dHook.jar1 id
* @return
*/
@GetMapping("/export")
Expand Down Expand Up @@ -101,33 +95,4 @@ public ResponseEntity<Object> export(@RequestParam(value = "id") String id) thro
return HttpUtil.responseSource(fileName, resource, jar_new.length);
}

@GetMapping("/get-plugins")
public String getPlugins(HttpServletResponse response) throws IOException {
Map<String, String> pluginJarMap = PluginUtil.pluginJarMap;
ArrayList<String> list = new ArrayList<>();
for (Map.Entry<String, String> plugin : pluginJarMap.entrySet()) {
String name = plugin.getKey();
list.add(name);
}
return GsonUtil.toJsonString(list);
}

@GetMapping("/getPluginByName")
public ResponseEntity<Object> getPlugins(String name) {
Map<String, String> pluginJarMap = PluginUtil.pluginJarMap;
String path = pluginJarMap.get(name);
if (path == null) return ResponseEntity.badRequest().body("0");

File file = new File(path);

try {
InputStreamResource inputStreamResource = new InputStreamResource(new FileInputStream(file));
return HttpUtil.responseSource(name + ".jar", inputStreamResource, file.length());
} catch (Exception e) {
return ResponseEntity.badRequest().body("0");
}
}



}
2 changes: 0 additions & 2 deletions src/main/java/com/keven1z/controller/PluginController.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import com.keven1z.service.IPluginService;
import com.keven1z.utils.HttpUtil;
import com.keven1z.utils.JarUtil;
import com.keven1z.utils.PluginUtil;
import dHook.IDHookExtenderCallbacks;

import org.springframework.core.io.InputStreamResource;
Expand All @@ -21,7 +20,6 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;


/**
Expand Down
3 changes: 1 addition & 2 deletions src/main/java/com/keven1z/utils/JarUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import org.springframework.core.io.support.ResourcePatternResolver;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
Expand Down Expand Up @@ -57,7 +56,7 @@ public static byte[] updateField(String className, String fieldName, String fiel
changeNodes.add(classNode);
}
// String inJarPath = file.getAbsolutePath();
// String outJarPath = System.getProperty("java.io.tmpdir") + File.separator +"dHook.jar";
// String outJarPath = System.getProperty("java.io.tmpdir") + File.separator +"dHook.jar1";
return JarLoader.saveToJar(agent, changeNodes);
}

Expand Down
Binary file modified src/main/resources/agent/dHook.jar
Binary file not shown.
6 changes: 3 additions & 3 deletions src/main/resources/static/js/hook.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ var TableInit = function () {
paginationPreText: "上一页",
paginationNextText: "下一页",
onClickRow: function (row, $element) {
console.log("click:" + row["id"])
// console.log("click:" + row["id"])
},
columns: [
{
Expand Down Expand Up @@ -80,7 +80,7 @@ var TableInit = function () {
return oTableInit;
};

function refresh() {
function refresh_hook() {
$('#tb_departments').bootstrapTable('refresh', {
query: {
pageNumber: 1
Expand All @@ -100,7 +100,7 @@ function del_hook() {
url: "/hook/del?hookId=" + id,
type: "get",
success: function (data) {
refresh();
refresh_hook();
},
error: function () {
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/templates/hook.html
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ <h4 class="modal-title">添加hook点</h4>
dataType: 'html',
contentType: 'application/json',
}).done(function (data) {
refresh();
refresh_hook();
setTimeout(function () {
$('#add_hook_dialog').modal('hide');
}, 200);
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ <h4 class="modal-title">添加应用</h4>
// valign: 'middle',
width: '200px',
formatter: function (value, row, index) {
return "<button class=\"btn btn-primary\" onclick=\"export_agent('" + row.id + "')\" >获取agent</button><a class=\"btn btn-primary\" href='/hook.html?id=" + row.id + "' >编辑Hook</a>";
return "<button class=\"btn btn-primary\" onclick=\"export_agent('" + row.id + "')\" >下载agent</button><a class=\"btn btn-primary\" href='/hook.html?id=" + row.id + "' >编辑Hook</a>";
}
}
]
Expand Down

0 comments on commit 7a02b81

Please sign in to comment.