JAVA反序列化基础之 注解 反射 类加载
Avienma
- 关注
JAVA反序列化基础之 注解 反射 类加载

1.注解
注解:Annotation
定义
不是程序本身,不是必要的,可以对程序做出解释,可以被其他程序读取。
格式:以 @ 开头,后面跟注解名,还有可以加参数值。
内置注解
@Override:表示重写父类的方法
@Deprecated:表示不推荐使用的,因为它很危险或者存在更好的选择
@SuppressWarnings:用于抑制编译时的警告信息
import java.util.ArrayList;
import java.util.List;
public class AnTest {
// @Override , 重写的注解
@Override
public String toString() {
return super.toString();
}
// 不推荐程序员使用,但是可以被使用,或者存在更好的方式
@Deprecated
public static void testDe(){
System.out.println("Deprecated");
}
// 用于抑制编译时的警告信息
@SuppressWarnings("all")
public static void testSu(){
List list = new ArrayList();
}
public static void main(String[] args) {
testDe();
}
}
自定义注解与元注解
元注解:
负责解释注解的 注解
@Target:用于描述注解的使用范围,用 value 赋值
@Retention:用于描注解的生命周期,用 value 赋值,默认是 RUNTIME,SOURCE < CLASS < RUNTIME(编译的时候 < 类中 < 运行的时候)
@Documented:表示注解是否生成在 Javadoc中
@Inherited:表示子类是否继承父类的注解
import java.lang.annotation.*;
public class AnTest2 {
@MyAnnotation
public void test(){
}
}
// 指定注解可以在那些地方使用,此处指定了只能在方法里使用
@Target(value = ElementType.METHOD)
// 表示注解在什么地方有效,此处指定了运行时
@Retention(value = RetentionPolicy.RUNTIME)
// 表示是否将我们的注解生成在javadoc文档中
@Documented
// 子类是否继承父类的注解
@Inherited
// 自定义注解
@interface MyAnnotation{
}
创建一个自定义注解实例:
import java.lang.annotation.*;
public class AnTest2 {
@MyAnnotation(teamName = "昆仑云安全团队")
public void test(){
}
}
@Target(value = ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
// 自定义注解
@interface MyAnnotation{
// 注解的参数:参数类型 + 参数名()
String teamName() default "昆仑云安全";
int startDate() default 2022;
}
2.反射
Java安全可以从反序列化漏洞开始说起,反序列化漏洞又可以从反射开始说起
定义
java本身是一种静态语言,但是可以通过反射机制,获得类似于动态语言的特性,但是同时,也会导致一些不安全性。
反射机制允许程序执行时,通过Reflection API 获取任何类的内部信息,并可以直接操作任意对象内部的属性和方法可以取到 private 修饰的属性方法)。
加载完类后,堆内存的方法区就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了完整的类的结构信息,这个对象就像一面镜子,我们可以通过这个对象来看到类的结构,所以形象的称之为反射。
反射的优缺点:
- 优点:可实现动态创建对象和编译,灵活性强
- 缺点:对性能有影响
获取反射对象:
package top.imustctf6;
public class Test01 {
public static void main(String[] args) throws ClassNotFoundException {
// 通过反射获取类的Class对象
Class c1 = Class.forName("top.imustctf6.User");
System.out.println(c1);
}
}
// 实体类
class User{
// 定义属性私有
private String name;
private int id;
private int age;
public User(){
}
public User(String name ,int id ,int age){
this.name =name;
this.id = id;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
获取Class类的几种方法。
package top.imustctf6;
public class Test01 {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Student();
System.out.println(person.name);
// way1:通过对象获得
Class c1 = person.getClass();
System.out.println(c1.hashCode());
// way2:forname获得
Class c2 = Class.forName("top.imustctf6.Student");
System.out.println(c2.hashCode());
// way3:通过类名.class获得
Class c3 = Student.class;
System.out.println(c3.hashCode());
// way4:基本内置类型的包装类都有一个Type属性,比如获取Integer对象的TYPE属性
Class c4 = Integer.TYPE;
System.out.println(c4);
// 获得c1的父类类型
Class c5 = c1.getSuperclass();
System.out.println(c5);
}
}
class Person{
public String name;
public Person(){
}
public Person(String name){
this.name = name;
}
}
class Student extends Person{
public Student(){
this.name = "学生";
}
}
对于反射的更深层次的理解,java类加载内存分析。
- 堆:存放 new的对象和数组,可被共享,不会存放别的对象引用
- 栈:存放 基本变量类型(包含其具体数值)和引用对象的变量会存放这个引用在堆里的具体地址
- 方法区(特殊的堆):包含所有的 class类和 static 变量
public class Test02 {
public static void main(String[] args) {
A a = new A();
System.out.println(A.m);
/*
1.加载到内存,产生一个类对应的class对象
2.链接,链接结束后 m = 0
3.初始化,合并static,使用类构造器(JVM里的操作)
<clinit>{
System.out.println("A类静态代码块初始化");
m = 300;
+...
m = 100;
}
可以很清楚的看到循序执行的次序,合并之后,m = 100
*/
}
}
class A{
static {
System.out.println("A类静态代码块初始化");
m = 100;
}
static int m = 0;
public A(){
System.out.println("A类的无参构造初始化");
}
}
--------------------------
输出:
100
3.分析类的初始化
主动引用
这里我们需要注意的是:反射也会导致主动引用,并且需要注意类的主动初始化顺序。
package top.imustctf6;
import org.w3c.dom.css.CSSUnknownRule;
public class Test03 {
static {
System.out.println("Main类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
//Son son = new Son();
// 反射也会产生主动引用
Class.forName("top.imustctf6.Son");
}
}
class Father{
static {
System.out.println("父类被加载");
}
}
class Son extends Father{
static {
System.out.println("子类被加载");
}
}
---------------------------------------------------------
输出:
Main类被加载
父类被加载
子类被加载
被动引用
实例程序:
public class Test03 {
static {
System.out.println("Main类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
// 子类调用父类的属性,不会初始化加载子类
// 输出:Main类被加载、父类被加载、昆仑云安全
// System.out.println(Son.b);
// 调用子类的常量池内容,子类与父类均不会加载
// 因为常量在链接阶段就已经存入调用类的常量池中了
// 输出:Main类被加载、521
System.out.println(Son.M);
}
}
class Father{
static String b = "昆仑云安全";
static {
System.out.println("父类被加载");
}
}
class Son extends Father{
static {
System.out.println("子类被加载");
}
// 子类常量池
static final int M =521;
}
扩展内容:类加载器。
类加载器的种类:
public class Test04 {
public static void main(String[] args) {
// 获取系统类的加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
// 获取系统类加载器的父类加载器 --> 扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
// 获取爷爷加载器,根加载器,无法直接获取
ClassLoader grandfather = parent.getParent();
System.out.println(grandfather);
}
}
------------------------------------------------
输出:
jdk.internal.loader.ClassLoaders$AppClassLoader@63947c6b
jdk.internal.loader.ClassLoaders$PlatformClassLoader@776ec8df
null
如果你想获得当前类的类加载器,可以如下操作:(使用反射机制)
ClassLoader classLoader = Class.forName("top.imustctf6.Test04").getClassLoader();
System.out.println(classLoader);
4.反射的应用
我们有一个User类如下:
package top.imustctf6;
public class User {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
private String name;
private int age;
private int id;
public User(){}
public User(String name,int age,int id){
this.name = name;
this.age = age;
this.id = id;
}
public void run(){
System.out.println("run");
}
public void book(){
System.out.println("book!");
}
public void time(){
System.out.println("time!");
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", id=" + id +
'}';
}
}
获取类的运行时结构。
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test04 {
public static void main(String[] args) throws ClassNotFoundException {
Class c1 = Class.forName("top.imustctf6.User");
// 获得类的名字
System.out.println(c1.getName()); // 获得包名 + 类名
System.out.println(c1.getSimpleName()); // 获得类名
System.out.println("====================================");
// 获得类的属性
Field[] fields = c1.getFields(); // 获得public属性值
fields = c1.getDeclaredFields(); // 获得全部属性值
for (Field field : fields){
System.out.println(field);
}
System.out.println("====================================");
// 获得类的方法
Method[] methods = c1.getMethods(); // 获得本类及其父类的所有public方法
for (Method method : methods) {
System.out.println("getMethods方法" + method);
}
methods = c1.getDeclaredMethods(); // 获得本类的方法
for (Method method : methods) {
System.out.println("getDeclaredMethods" + method);
}
System.out.println("====================================");
// 获取类的构造器
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) {
System.out.println("getConstructors" + constructor);
}
constructors = c1.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println("getDeclaredConstructors" + constructor);
}
}
}
对应的输出信息:
动态创建对象执行。
通过反射,构造器创建对象。
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Test05 {
public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
Class c1 = Class.forName("top.imustctf6.User");
// 通过构造器创建对象
Constructor constructor = c1.getDeclaredConstructor(String.class,int.class,int.class);
User user01 = (User)constructor.newInstance("昆仑云安全",1,1);
System.out.println(user01);
}
}
通过反射操作方法:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test05 {
public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
Class c1 = Class.forName("top.imustctf6.User");
// 通过反射调用普通方法
User user02 = (User)c1.newInstance();
// 通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName",String.class);
// invoke 激活使用(对象,"方法的值")
setName.invoke(user02,"昆仑云安全");
System.out.println(user02.getName());
}
}
**成功调用方法:
通过反射操作属性:
注意:java中默认无法对私有属性进行操作,我们可以关闭安全检测机制来绕过对应的限制,通过反射,成功访问到类的私有属性。
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test05 {
public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException {
Class c1 = Class.forName("top.imustctf6.User");
// 通过反射来操作私有属性
User user04 = (User)c1.newInstance();
Field name = c1.getDeclaredField("name");
// 关闭程序的安全检测
name.setAccessible(true);
// 设置属性值
name.set(user04,"昆仑云安全");
System.out.println(user04.getName());
}
}
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 Avienma 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
一次恶意代码分析记录
2022-07-10
一次Docker逃逸引起域控权限的丢失
2022-05-01
浅析内网渗透中协议基础与常见手法
2022-04-23
文章目录