在看了部分的涉及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的方法前后,对具体的代码进行处理就可以了。