Java代码审计之Java的类反射机制

Java的类反射机制

教材内容

一、基本概念
1、序列化与反序列化

(1)序列化:将对象写入IO流中,ObjectOutputStream类的 writeObject() 方法可以实现序列化。

(2)反序列化:从IO流中恢复对象,ObjectInputStream 类的 readObject() 方法用于反序列化。

(3)意义:序列化机制允许将实现序列化的Java对象转换位字节序列,这些字节序列可以保存在磁盘上,或通过网络传输,以达到以后恢复成原来的对象。序列化机制使得对象可以脱离程序的运行而独立存在。

image-20220425162731793

(4)序列化与反序列化是让 Java 对象脱离 Java 运行环境的一种手段,可以有效的实现多平台之间的通信、对象持久化存储。主要应用在以下场景:

HTTP:多平台之间的通信,管理等,也可以用于流量带外。

RMI:是 Java 的一组拥护开发分布式应用程序的 API,实现了不同操作系统之间程序的方法调用。值得注意的是,RMI 的传输 100% 基于反序列化,Java RMI 的默认端口是 1099 端口。

JMX:JMX 是一套标准的代理和服务,用户可以在任何 Java 应用程序中使用这些代理和服务实现管理,中间件软件 WebLogic 的管理页面就是基于 JMX 开发的,而 JBoss 则整个系统都基于 JMX 构架。

(5)Java代码审计思路

如果是Java原生类,则需要入口类有readObject方法,同时实现了序列化接口,使其可以进行有效的反序列化,此时如果存在DNS解析,或者实现反序列化(利用Runtime对象进行类反射操作)。

需要有最终的执行函数(可以执行代码或者命令),比如Runtime.getRuntime().exec,ProcessBuilder().start,getHostAddress,文件读写……等等,这些函数需要自己平常去收集,这样审计起来会更得心应手。

https://blog.youkuaiyun.com/weixin_45864705/article/details/127057380

2、Java类反射机制

(1)反射机制的作用: 通过java语言中的反射机制可以操作字节码文件(可以读和修改字节码文件),可以通过另外的方式调用到类的属性和方法,甚至私有属性和方法。可以把一个类的调用一个方法的调用编程字符串来调用。

(2)反射机制的相关类在java.lang.reflect.*包下面。

(3)反射机制的相关类有哪些:Constructor、Field、Method、Class等类

(4)java.lang.Class 代表字节码文件,代表整个类

(5)java.lang.reflect.Method 代表字节码中的方法字节码,代表类中的方法java.lang.reflect.Constructor 代表字节码中的构造方法字节码,代表类中的构造方法java.lang.reflect.Field 代表字节码中的属性字节码,代表类中的属性。

如果有多个文件的同时,不区分.java 文件还能正常访问吗

public class RunCalc {    
    public static void main(String[] args) throws Exception{        
        // 正常运行一个应用程序        
        Runtime calc = Runtime.getRuntime();         
        calc.exec("calc");         
        // 使用类反射机制运行一个应用 程序        
        Class clazz = Class.forName("java.lang.Runtime");   
        // 也可以使用 java.lang.Runtime.class获取        
        Object obj = clazz.getMethod("getRuntime", null).invoke(clazz,null);        
        clazz.getMethod("exec",String.class).invoke(obj, "calc.exe");    
    }
}

image-20250112220912096

如果在另一个文件中和出现了相同的类,那么就会报错,编译的时候根本不会通过

(6)Java中为什么要使用反射机制,直接创建对象不是更方便?
如果有多个类,每个用户所需求的对象不同,直接创建对象,就要不断的去new一个对象,非常不灵活。而java反射机制,在运行时确定类型,绑定对象,动态编译最大限度发挥了java的灵活性。

(7)获取成员变量

Field[] field = c.getDeclaredFields()    // getFields则获取public属性

(8)获取并调用方法

Method[] method = c.getDeclaredMethods()   // getMethods则获取public方法

(9)获取构造方法

Constructor[] constructor = c.getDeclaredConstructors();   // getConstructors则获取所有构造方法

(10)访问私有属性

f.setAccessible(true);
二、类反射机制实践
1、普通类调用
package com.woniuxy.core;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import javax.activation.FileDataSource;
public class Reflect {    
    public static void main(String[] args) throws Exception{
        //        Class clazz = Class.forName("java.lang.Runtime");   // 也可以使用 java.lang.Runtime.class获取
        //        Object obj = clazz.getMethod("getRuntime", null).invoke(clazz,null);
        //        String[] app = {"calc.exe"}; 
        //        clazz.getMethod("exec",String.class).invoke(obj, app);        
        Class clazz = Test.class;
        //        Object obj = clazz.newInstance();  
        // 如果是默认构造方法,使用此方法        
        Constructor constructor = clazz.getDeclaredConstructor(int.class);        
        Object obj = constructor.newInstance(20000);        
        Method method = clazz.getMethod("setPrice", int.class);        
        int price = (int)method.invoke(obj, 5000);        
        System.out.println(price);        
        Field[] fields = clazz.getDeclaredFields();        
        for (Field field: fields) {            
            // 反射时可以访问私有属性            
            field.setAccessible(true);            
            if (field.getName() == "addr") {                
                field.set(obj, "火车南站");            
            }            
            System.out.println(field.get(obj));        
        }        
        // 反射时访问私有方法       
        Method method2 = clazz.getDeclaredMethod("getAddr", null);        
        method2.setAccessible(true);        
        method2.invoke(obj, null);    
    }
}

class Test {    
    public String name = "蜗牛学院";    
    public int age = 8;    
    private String addr = "大鼎世纪广场";    
    private int price = 10000;    
    public Test(int price) {        
        this.price = price;    
    }    
    public int setPrice(int price) {        
        System.out.println("新价格为:" + price);        
        return price;    
    }    
    private void getAddr() {        
        System.out.println("私有方法:" + this.addr);    
    }
}
2、调用ProcessBuilder

如果要执行命令,也可以反射ProcessBuilder类(注意构造方法)

Class clazz = Class.forName("java.lang.ProcessBuilder");Constructor c = clazz.getConstructor(List.class);Object o = c.newInstance(Arrays.asList("calc.exe"));Method m = clazz.getMethod("start");m.invoke(o, null);

如果用ProcessBuilder的另外一个构造方法:ProcessBuilder(String… command),可变长参数是一个数组的形式存在。

Class clazz = Class.forName("java.lang.ProcessBuilder");Constructor c = clazz.getConstructor(String[].class);Object o = c.newInstance(new String[][]{{"calc.exe"}});Method m = clazz.getMethod("start");m.invoke(o, null);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值