java.lang.ProcessBuilder常用于执行本地系统命令,为了便于理解Hook机制,这里以Hook ProcessBuilder类的start方法作为示例,演示如何Hook机制的工作原理。
示例 - 未经Hook的原始java.lang.ProcessBuilder类代码片段:
package java.lang;import java.io.IOException;import java.util.List;public final class ProcessBuilder {private List<String> command;// 省略其他不相关类和成员变量public Process start() throws IOException {// 省去其他无关代码return ProcessImpl.start(command, environment, dir, redirects, redirectErrorStream);}}
ProcessBuilder类可以调用UNIXProcess/ProcessImpl类的native方法执行本地系统命令,默认情况下可以被任意的Java类调用,所以存在安全问题。RASP使用Agent机制动态修改了ProcessBuilder类的start方法字节码,在方法体的前后插入RASP防御代码,当start方法被调用时因为程序逻辑已被RASP更改,必须先执行RASP的防御逻辑之后才能够执行start方法的原始业务逻辑,如果RASP调用内部的检测逻辑后发现可能存在恶意攻击,RASP会终止start方法执行逻辑,从而避免了恶意攻击。
示例 - RASP的Hook逻辑代码片段:
package org.javaweb.rasp.agent;import org.javaweb.rasp.agent.commons.RASPLogger;import org.javaweb.rasp.agent.hooks.advice.RASPMethodAdvice;import org.javaweb.rasp.agent.hooks.annotation.RASPClassHook;import org.javaweb.rasp.agent.hooks.annotation.RASPMethodHook;import org.javaweb.rasp.agent.utils.ClassUtils;import org.javaweb.rasp.loader.hooks.RASPHookResult;/*** Hook 本地命令执行*/@RASPClassHookpublic class LocalCommandHook {/*** Hook 通用的ProcessBuilder类*/@RASPMethodHook(className = "java.lang.ProcessBuilder", methodName = "start")public static class ProcessBuilderHook extends RASPMethodAdvice {@Overridepublic RASPHookResult<?> onMethodEnter() {Object obj = getThisObject();try {// 获取ProcessBuilder类的command变量值List<String> command = ClassUtils.getFieldValue(obj, "command");// 将执行的系统命令转换成字符串数组String[] commands = command.toArray(new String[command.size()]);// 调用processCommand方法,检测执行的本地命令合法性return LocalCommandHookHandler.processCommand(commands, obj, this);} catch (Exception e) {RASPLogger.log(AGENT_NAME + "获取ProcessBuilder类command变量异常:" + e, e);}return new RASPHookResult(RETURN);}}// 省略其他本地命令执行Hook点}
示例 - 经过RASP修改后的java.lang.ProcessBuilder类
package java.lang;import org.javaweb.rasp.loader.hooks.RASPHookHandlerType;import org.javaweb.rasp.loader.hooks.RASPHookProxy;import org.javaweb.rasp.loader.hooks.RASPHookResult;import java.io.IOException;import java.util.List;public final class ProcessBuilder {private List<String> command;public Process start() throws IOException {// 生成Object数组对象,存储方法参数值Object[] parameters = new Object[]{};// 生成try/catchtry {// 调用RASP方法方法进入时检测逻辑RASPHookResult<?> enterResult = RASPHookProxy.onMethodEnter(parameters, ...);String HandlerType = enterResult.getRaspHookHandlerType().toString();if (RASPHookHandlerType.REPLACE_OR_BLOCK.toString().equals(HandlerType)) {// 如果RASP检测结果需要阻断或替换程序执行逻辑,return RASP返回结果中设置的返回值return (Process) enterResult.getReturnValue();} else if (RASPHookHandlerType.THROW.toString().equals(HandlerType)) {// 如果RASP检测结果需要往外抛出异常,throw RASP返回结果中设置的异常对象throw (Throwable) enterResult.getException();}// 执行程序原逻辑,执行本地系统命令并返回Process对象Process methodReturn = ProcessImpl.start(command, environment, dir, redirects, redirectErrorStream);// 调用RASP方法方法退出时检测逻辑,同onMethodEnter,此处省略对应代码return methodReturn;} catch (Throwable t) {// 调用RASP方法方法异常退出时检测逻辑,同onMethodEnter,此处省略对应代码}}}
