javassist技术研究&Sql注入检测

AOP为Aspect Oriented Programming的缩写 面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
日志记录,性能统计,安全控制,事务处理

Javassist是编辑字节码的Java类库.
通过使用Javassist可以使Java程序在运行时定义一个新的类,并且在JVM加载类文件时修改它.
提供两个级别的API:源码级别和字节码级别。

ClassLoader是java的核心组件,所有的class都是由ClassLoader进行加载的。ClassLoader负责通过各种方式将Class信息的二进制数据流读入JVM内部,转换为一个与目标类对应的java.lang.Class对象实例,然后交给虚拟机进行链接、初始化等操作。

那么使用Javassist生成一个新类也包含类似的步骤:

获取ClassPool对象(ClassPool代表CtClass的容器)
通过ClassPool对象构建一个CtClass对象(一个CtClass代表一个Class的容器)(确定类名)
通过CtField对象向类中添加类字段(添加字段)
通过CtConstructor对象向类中添加构造函数(添加构造函数)
通过CtMethod对象向类中添加方法(添加成员函数)

添加maven依赖

<dependency>
      <groupId>org.javassist</groupId>
       <artifactId>javassist</artifactId>
       <version>3.28.0-GA</version>
</dependency>

相关代码知识
ClassPool:存放CtClass的容器
ClassPool是存放CtClass的容器,CtClass只能通过ClassPool提供的方法获取,其中包括get()&makeClass()方法;ClassPool通过HashTable来缓存已经创建好的CtClass对象,其中键为className,值为CtClass对象。
ClassPool中如果缓存大量的CtClass对象,可能会消耗大量缓存;为了避免这种情况的发生,我们可以调用CtClass.detach()方法,该方法将会删除ClassPool中相应的CtClass对象,释放空间;如果我们调用get()方法获取被删除的CtClass对象,那么ClassPool会重新生成相应的CtClass对象
toClass():将CtClass对象转换为Class对象
importPackage():将Java包导入到ClassPool中,方便其通过package查找到对应的类
appendClassPath, insertClassPath : 将一个ClassPath加到类搜索路径的末尾位置 或 插入到起始位置。通常通过该方法写入额外的类搜索路径,以解决多个类加载器环境中找不到类的尴尬;


CtClass:代表一个class or interface or annotation

CtField:代表class中的某个字段
CtConstructor:代表class的构造函数

CtMethod:代表class的包含的方法
insertBefore : 在方法的起始位置插入代码;
insterAfter : 在方法的所有 return 语句前插入代码以确保语句能够被执行,除非遇到exception;
insertAt : 在指定的位置插入代码;
setBody : 将方法的内容设置为要写入的代码,当方法被 abstract修饰时,该修饰符被移除;
make : 创建一个新的方法。

实例代码
插桩操作

import javassist.*;

if("java/sql/Statement".equals(className)){
     //通过类名称获取类
     CtClass ctClass = ClassPool.getDefault().getCtClass(className.replace('/', '.'));
     //获取已申明方法
     CtMethod ctMethod = ctClass.getDeclaredMethod("executeQuery");
     //方法前插入语句
     ctMethod.insertBefore();
     //新增方法
     ctClass.addMethod(CtMethod.make(" 方法体 "), ctClass));
     //转换为字节码
     byte[] byteCode = ctClass.toBytecode();
     ctClass.detach();
     return byteCode;
    
    // 添加getter和setter方法
        CtMethod setUsername = new CtMethod(CtClass.voidType, "setUsername", new CtClass[] {pool.get("java.lang.String")},
                ctClass);
        setUsername.setModifiers(Modifier.PUBLIC);
        setUsername.setBody("{$0.username = $1;}");
        ctClass.addMethod(setUsername);

        CtMethod getUsername = new CtMethod(pool.get("java.lang.String"), "getUsername", new CtClass[] {}, ctClass);
        getUsername.setModifiers(Modifier.PUBLIC);
        getUsername.setBody("{return $0.username;}");
        ctClass.addMethod(getUsername);
        
        Class<?> user = ctClass.toClass();
        Object instance = user.getDeclaredConstructors()[0].newInstance();
        System.out.println(instance);

        // 获取String的ctClass类
        CtClass[] method_arguments = new CtClass[1];
        method_arguments[0] = classPool.get("java.lang.String");
        CtMethod ctMethod_query = clazz.getDeclaredMethod("DruidPooledPreparedStatement", method_arguments);

}

捕获异常操作:

catch (Throwable e) {
       System.err.println("出错了:" + e.getMessage());
       e.printStackTrace();
}

您还可以直接加载CtClass:

获取字节码: byte[] b = cc.toBytecode();
直接加载CtClass: Class clazz = cc.toClass();
Annotation[][] annotations = method.getParameterAnnotations();

annotations[0]代表第一个参数,annotations[1]代表第二个参数。
像第一个参数的话,它有2个注解,annotations[0][0]表示第一个注解,annotations[0][1]表示第二个注解。

经验:

1. 被监控的主程序需要javassit依赖
2. .MF文件内容 需要写全

关于jdbc中Statement:
statement 用于执行不带参数的SQL语句
​preperStatement 用于执行带参数的SQL语句可以防SQL注入
​callableStatement 用于执行存储过程的调用

Statement 接口提供了三种执行 SQL 语句的方法:executeQuery、executeUpdate 和 execute。使用哪一个方法由 SQL 语句所产生的内容决定。
1.方法executeQuery : 用于产生单个结果集的语句,例如 SELECT 语句。
2.方法executeUpdate:用于执行 INSERT、UPDATE 或 DELETE 语句以及 SQL DDL(数据定义语言)语句,例如 CREATE TABLE 和 DROP TABLE。
3.方法execute:用于执行返回多个结果集、多个更新计数或二者组合的语句。

要执行SQL语句,必须获得java.sql.Statement实例,Statement实例分为以下3种类型:
1、执行静态SQL语句。通常通过Statement实例实现。  SQL语句在程序运行前被编译
2、执行动态SQL语句。通常通过PreparedStatement实例实现。 SQL语句在程序运行时被编译
3、执行数据库存储过程。通常通过CallableStatement实例实现。

redefineClasses 是自己提供字节码文件替换掉已存在的 class 文件
retransformClasses 是在已存在的字节码文件上修改后再进行替换

Web安全漏洞学习平台:WebGoat (java 15)
WebGoat是一个用来演示Web浏览器中典型安全漏洞的应用程序,其目的是在应用程序安全审查的上下文中系统、完整地介绍如何测试和利用这些安全漏洞。

SQL注入类型:
SQL注入方式主要有以下几种:

①同义反复:将恶意代码插入到多个条件语句中,使SQL语句的条件子句部分总是为真,致使原条件失效,主要用于绕过身份验证。
②逻辑错误查询:向被测系统提交错误的SQL语句,通过分析系统返回的错误信息来判断系统内部信息。
③联合查询:通过在SQL语句中加入“UNION”求并集子句来获得额外信息。
④复合查询:使用“;”将SQL查询、插入、删除等操作连接并执行。
⑤存储过程:存储过程包含参数,在参数传递中存在SQL注入。
⑥推理:构造条件互相对立的SQL语句,可分为条件盲注和时间盲注。
⑦编码替换:利用ASCII编码、十六进制编码等方式转换语句进行注入。
⑧按具体的攻击手段,常用的SQL注入方法大致有Union注入、Boolean注入、报错注入、时间注入、堆叠查询注入、二次注入、宽字节注入、Cookie注入、Base64注入、XFF注入等。

时间盲注是指基于时间的盲注,也叫延时注入,根据页面的响应时间来判断是否存在注入。

@interface不是接口是注解类,在jdk1.5之后加入的功能,使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节
@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。

public @interface Component {
	String value() default "";
}

使用 该注解的地方,需要给属性赋值,如果属性定义时,带有default,则可以不用赋值

@Retention 用来确定这个注解的生命周期;
@Target 指定注解使用的目标范围(类、方法、字段等)

//保留的环境
@Retention(RUNTIME)

public enum RetentionPolicy {
    SOURCE,            /* Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了  */
    CLASS,             /* 编译器将Annotation存储于类对应的.class文件中。默认行为  */
    RUNTIME            /* 编译器将Annotation存储于class文件中,并且可由JVM读入 */
}

//注释起作用的位置,此处表示它只能给类、接口、枚举注解
@Target(TYPE)

public enum ElementType {
    TYPE,               /* 类、接口(包括注释类型)或枚举声明  */
    FIELD,              /* 字段声明(包括枚举常量)  */
    METHOD,             /* 方法声明  */
    PARAMETER,          /* 参数声明  */
    CONSTRUCTOR,        /* 构造方法声明  */
    LOCAL_VARIABLE,     /* 局部变量声明  */
    ANNOTATION_TYPE,    /* 注释类型声明  */
    PACKAGE             /* 包声明  */
}

@Documented:说明该注解将会被包含在JavaDoc文档中 导出(export–java doc)
这个注解对程序没什么影响,只是在对于生成帮助文档时有帮助。

SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。一般情况下,SSRF是要目标网站的内部系统。(因为他是从内部系统访问的,所有可以通过它攻击外网无法访问的内部系统,也就是把目标网站当中间人)

一般情况下,SSRF攻击的目标是从外网无法访问的内部系统。


版权声明:本文为qq_35076836原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。