在看了部分的涉及mongo的代码后,发现很多是用于错误处理的,那么能否直接删除这部分呢。。
(这个问题之后在看)
0x01 OpenRasp的Hook
首先有一个AbstractClassHook类
匹配是否是需要检测的类、检测的类型、检测的函数
利用insertbefore插入检测的代码
/** * 在目标类的目标方法的入口插入相应的源代码 * * @param ctClass 目标类 * @param methodName 目标方法名称 * @param desc 目标方法的描述符号 * @param src 待插入的源代码 */ public void insertBefore(CtClass ctClass, String methodName, String desc, String src) throws NotFoundException, CannotCompileException { LinkedList<CtBehavior> methods = getMethod(ctClass, methodName, desc, null); if (methods != null && methods.size() > 0) { insertBefore(methods, src); } else { if (Config.getConfig().isDebugEnabled()) { LOGGER.info("can not find method " + methodName + " " + desc + " in class " + ctClass.getName()); } } } /** * 在目标类的目标方法的入口插入相应的源代码 * 可排除一定的方法 * * @param ctClass 目标类 * @param methodName 目标方法名称 * @param excludeDesc 排除的方法描述符 * @param src 待插入的源代码 */ public void insertBeforeWithExclude(CtClass ctClass, String methodName, String excludeDesc, String src) throws NotFoundException, CannotCompileException { LinkedList<CtBehavior> methods = getMethod(ctClass, methodName, null, excludeDesc); if (methods != null && methods.size() > 0) { insertBefore(methods, src); } else { if (Config.getConfig().isDebugEnabled()) { LOGGER.info("can not find method " + methodName + " exclude desc:" + excludeDesc + " in class " + ctClass.getName()); } } } private void insertBefore(LinkedList<CtBehavior> methods, String src) throws CannotCompileException { for (CtBehavior method : methods) { if (method != null) { insertBefore(method, src); } } } /** * 在目标类的一组重载的目标方法的入口插入相应的源代码 * * @param ctClass 目标类 * @param methodName 目标方法名称 * @param allDesc 目标方法的一组描述符 * @param src 待插入的源代码 */ public void insertBefore(CtClass ctClass, String methodName, String src, String[] allDesc) throws NotFoundException, CannotCompileException { for (String desc : allDesc) { insertBefore(ctClass, methodName, desc, src); } }
这里四个的区别是1和2指定方法的名称,1是确定方法的描述符,2是排除方法的描述符号;3是遍历一组方法;4是遍历一个方法的一组描述符。
insertafter做检测后的处理
/** * 在目标类的目标方法的出口插入相应的源代码 * * @param ctClass 目标类 * @param methodName 目标方法名称 * @param desc 目标方法的描述符号 * @param src 待插入的源代码 * @param asFinally 是否在抛出异常的时候同样执行该源代码 */ public void insertAfter(CtClass ctClass, String methodName, String desc, String src, boolean asFinally) throws NotFoundException, CannotCompileException { LinkedList<CtBehavior> methods = getMethod(ctClass, methodName, desc, null); if (methods != null && methods.size() > 0) { for (CtBehavior method : methods) { if (method != null) { insertAfter(method, src, asFinally); } } } else { if (Config.getConfig().isDebugEnabled()) { LOGGER.info("can not find method " + methodName + " " + desc + " in class " + ctClass.getName()); } } } private LinkedList<CtBehavior> getConstructor(CtClass ctClass, String desc) { LinkedList<CtBehavior> methods = new LinkedList<CtBehavior>(); if (StringUtils.isEmpty(desc)) { Collections.addAll(methods, ctClass.getDeclaredConstructors()); } else { try { methods.add(ctClass.getConstructor(desc)); } catch (NotFoundException e) { // ignore } } return methods; } /** * 获取特定类的方法实例 * 如果描述符为空,那么返回所有同名的方法 * * @param ctClass javassist 类实例 * @param methodName 方法名称 * @param desc 方法描述符 * @return 所有符合要求的方法实例 * @see javassist.bytecode.Descriptor */ protected LinkedList<CtBehavior> getMethod(CtClass ctClass, String methodName, String desc, String excludeDesc) { if ("<init>".equals(methodName)) { return getConstructor(ctClass, desc); } LinkedList<CtBehavior> methods = new LinkedList<CtBehavior>(); if (StringUtils.isEmpty(desc)) { CtMethod[] allMethods = ctClass.getDeclaredMethods(); if (allMethods != null) { for (CtMethod method : allMethods) { if (method != null && !method.isEmpty() && method.getName().equals(methodName) && !method.getSignature().equals(excludeDesc)) methods.add(method); } } } else { try { CtMethod ctMethod = ctClass.getMethod(methodName, desc); if (ctMethod != null && !ctMethod.isEmpty()) { methods.add(ctMethod); } } catch (NotFoundException e) { // ignore } } return methods; } /** * 在目标类的目标方法的入口插入相应的源代码 * * @param method 目标方法 * @param src 源代码 */ public void insertBefore(CtBehavior method, String src) throws CannotCompileException { try { method.insertBefore(src); LOGGER.info("insert before method " + method.getLongName()); } catch (CannotCompileException e) { LogTool.traceError(ErrorType.HOOK_ERROR, "insert before method " + method.getLongName() + " failed: " + e.getMessage(), e); throw e; } } /** * (none-javadoc) * * @see com.baidu.openrasp.hook.AbstractClassHook#insertAfter(CtClass, String, String, String, boolean) */ public void insertAfter(CtClass invokeClass, String methodName, String desc, String src) throws NotFoundException, CannotCompileException { insertAfter(invokeClass, methodName, desc, src, false); } /** * 在目标类的目标方法的出口插入相应的源代码 * * @param method 目标方法 * @param src 源代码 * @param asFinally 是否在抛出异常的时候同样执行该源代码 */ public void insertAfter(CtBehavior method, String src, boolean asFinally) throws CannotCompileException { try { method.insertAfter(src, asFinally); LOGGER.info("insert after method: " + method.getLongName()); } catch (CannotCompileException e) { LogTool.traceError(ErrorType.HOOK_ERROR, "insert after method " + method.getLongName() + " failed: " + e.getMessage(), e); throw e; } }
insertafter做检测后的处理
1和2在查找对应的方法和描述符后插入代码,区别是是否在异常时插入代码,3在直接对找到的方法插入代码。
动态生成静态代码
/** * 获取调用静态方法的代码字符串 * * @param invokeClass 静态方法所属的类 * @param methodName 静态方法名称 * @param paramString 调用传入的参数字符串,按照javassist格式 * @return 整合之后的代码 */ public String getInvokeStaticSrc(Class invokeClass, String methodName, String paramString, Class... parameterTypes) { String src; String invokeClassName = invokeClass.getName(); String parameterTypesString = ""; if (parameterTypes != null && parameterTypes.length > 0) { for (Class parameterType : parameterTypes) { if (parameterType.getName().startsWith("[")) { parameterTypesString += "Class.forName(\"" + parameterType.getName() + "\"),"; } else { parameterTypesString += (parameterType.getName() + ".class,"); } } parameterTypesString = parameterTypesString.substring(0, parameterTypesString.length() - 1); } if (parameterTypesString.equals("")) { parameterTypesString = null; } else { parameterTypesString = "new Class[]{" + parameterTypesString + "}"; } if (isLoadedByBootstrapLoader) { src = "com.baidu.openrasp.ModuleLoader.moduleClassLoader.loadClass(\"" + invokeClassName + "\").getMethod(\"" + methodName + "\"," + parameterTypesString + ").invoke(null"; if (!StringUtils.isEmpty(paramString)) { src += (",new Object[]{" + paramString + "});"); } else { src += ",null);"; } src = "try {" + src + "} catch (Throwable t) {if(t.getCause() != null && t.getCause().getClass()" + ".getName().equals(\"com.baidu.openrasp.exceptions.SecurityException\")){throw t;}}"; } else { src = invokeClassName + '.' + methodName + "(" + paramString + ");"; src = "try {" + src + "} catch (Throwable t) {if(t.getClass()" + ".getName().equals(\"com.baidu.openrasp.exceptions.SecurityException\")){throw t;}}"; } return src; }
动态生成静态代码主要是以下几个方面
- 运行生成代码,不在编译时硬编码
- 适应不同的类、方法
- 选择不同的类加载器
0x02 反序列化的检测
public String getType() { return "deserialization"; } /** * (none-javadoc) * * @see com.baidu.openrasp.hook.AbstractClassHook#isClassMatched(String) */ @Override public boolean isClassMatched(String className) { return "java/io/ObjectInputStream".equals(className); } /** * (none-javadoc) * * @see com.baidu.openrasp.hook.AbstractClassHook#hookMethod(CtClass) */ @Override protected void hookMethod(CtClass ctClass) throws IOException, CannotCompileException, NotFoundException { String src = getInvokeStaticSrc(DeserializationHook.class, "checkDeserializationClass", "$1", ObjectStreamClass.class); insertBefore(ctClass, "resolveClass", "(Ljava/io/ObjectStreamClass;)Ljava/lang/Class;", src); } /** * 反序列化监检测点 * * @param objectStreamClass 反序列化的类的流对象 */ public static void checkDeserializationClass(ObjectStreamClass objectStreamClass) { if (objectStreamClass != null) { String clazz = objectStreamClass.getName(); if (clazz != null) { HashMap<String, Object> params = new HashMap<String, Object>(); params.put("clazz", clazz); HookHandler.doCheck(CheckParameter.Type.DESERIALIZATION, params); } } }
找的类名:java/io/ObjectInputStream
hookMethods中生成了具体调用的静态代码,利用insertBefore插入生成的代码到ctClass(具体的类),resolveClass(方法),“(Ljava/io/ObjectStreamClass;)Ljava/lang/Class;”是方法前面。
具体的检测
/** * 反序列化监检测点 * * @param objectStreamClass 反序列化的类的流对象 */ public static void checkDeserializationClass(ObjectStreamClass objectStreamClass) { if (objectStreamClass != null) { String clazz = objectStreamClass.getName(); if (clazz != null) { HashMap<String, Object> params = new HashMap<String, Object>(); params.put("clazz", clazz); HookHandler.doCheck(CheckParameter.Type.DESERIALIZATION, params); } } }
具体的:HookHandler#docheck();—>HookHandler#doCheckWithoutRequest();—>HookHandler#doRealCheckWithoutRequest();—>CheckerManager#check();
并且通过checkParameter设置的内容进行检测
0x03 小结
学习了rasp的hook,大概解决了之前的问题,为什么rasp拦截了反序列化对象的生成,还可以获取完整的代码运行链路,只需要在调用被hook的方法前后,对具体的代码进行处理就可以了。