Java反射与Fastjson的危险反序列化

什么是Java反射?

在前文中,我们有一行代码 Computer macBookPro = JSON.parseObject(preReceive,Computer.class);

这行代码是什么意思呢?看起来好像就是我们声明了一个名为 macBookPro 的 Computer 类,它由 fastjson 的 parseObject 方法将 preReceive 反序列化而来,但 Computer.class 是什么呢?

在 Java 中,Computer.class是一个引用,它表示了 Computer 的字节码对象(Class对象),这个对象被广泛应用于反射、序列化等操作中。那么为什么 parseObject 需要这个引用呢?首先 fastjson 是不了解类中的情况的,因此它需要一个方法来动态的获得类中的属性,那么 Java 的反射机制提供了这个功能。

Java reflect demo

我们先看一个 Java 反射的 Demo。

package org.example;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;public class JavaReflectDemo {public static void main(String[] args){// 获取Car类的Class对象,用于后续的反射操作Class<?> temp = Car.class;// 获得Car类的所有属性与方法和构造方法Field[] fields = temp.getDeclaredFields();Method[] methods = temp.getDeclaredMethods();Constructor<?>[] constructors = temp.getDeclaredConstructors();// 通过循环遍历获得类属性for (Field field : fields){System.out.println("Field: " + field.getName());}// 通过循环遍历获得方法名for (Method method : methods ) {System.out.println("Methods: " + method.getName());}// 通过双循环获得类的构造方法及其方法所需要的参数的数据类型for (Constructor<?> constructor : constructors) {System.out.println("Constructor:" + constructor.getName());Class<?>[] constructorParameterType = constructor.getParameterTypes();for (Class<?> parameterType : constructorParameterType) {System.out.println("Parameter type is:" + parameterType.getName());}}// 通过反射调用类方法}public static class Car{private int carLength;public String carName;private int carPrice = 50000;public Car(int carLength, String carName,int carPrice){this.carLength = carLength;this.carName = carName;this.carPrice = carPrice;}private void CarAnnounce() {System.out.println("China Car! Best Car!");System.out.println("The Car Price is " + this.carPrice);System.out.println("The Car Length is " + this.carLength);}private void CarType(){System.out.println("This function is still under development!");}}
}

反射调用类变量

上述代码中,我们有一个公共静态类 Car ,其中包含了私有和公共方法和属性,在主函数中通过反射获取了类的属性和方法以及构造方法,我们逐行分析代码。

  • Class<?> temp = Car.class; 这行代码用于获取 Car 的 Class 对象,Class 对象是整个反射操作的起点。那么 Class<?> 是什么意思呢?其实在这里这个问号指的是 temp 可以接收任意类型的类,我们也可以通过 Class<Car> 来接收 Class 对象。

  • getDeclaredFields() 是 Java 的反射操作,通过 Class 对象获得类中所有的属性,包括私有属性,它返回一个 Field[] 对象,实际上是一个包含类中所有属性的数组,但它被特定为 Field[] 对象。

  • getDeclaredMethods() 同理,获得类中所有的方法(但不包含构造方法),返回一个 Methods[] 数组。

  • getDeclaredConstructors() 用于获得类中所有的构造方法,Constructor<?>[] 的含义是,Constructor 是个泛型类,它的定义是 Constructor<T> ,这意味着它适用于任何类型的构造方法,通过使用通配符 <?> 表示这个数组接收任何类的构造方法,也就是表示了constructors 这个数组可以用于存储任意类的任意构造方法。

  • 获得了数组后,通过 Java 的 for-each 循环遍历数组并打印到屏幕。

运行结果如下。

反射调用类方法

简要将Demo中的代码修改如下。

// 通过循环遍历获得方法名for (Method method : methods) {// 直接调用类的静态方法if (method.getName().equals("CarType")) {method.invoke(null);}// 通过类的实例调用类方法if (method.getName().equals("CarAnnounce")){Car tempCar = new Car(1000,"Richard's car");method.invoke(tempCar);// 通过反射获得类字段,并修改字段值重新调用方法Field field = temp.getDeclaredField("carPrice");field.setAccessible(true);field.set(tempCar, 99999);method.invoke(tempCar);}System.out.println("Methods: " + method.getName());
}

我们可以通过反射直接调用类的方法,method.invoke(类的实例, 参数, 多个参数用逗号隔开),若是调用静态方法可以传递 null 代替类的实例,但如果调用的方法需要参数,我们需要严格得按照方法传入对应的参数。

我们还可以通过反射修改 private 属性,例如 Demo 中的 carPrice。运行结果如下。

我们将 carLength 使用 final 修饰符进行修饰,此时直接修改 carLength 会报错。如下图。

但通过反射我们可以修改 final 修饰符修饰后的属性。代码如下。

Field field2 = temp.getDeclaredField("carLength");
field2.setAccessible(true);Field modifiers = field2.getClass().getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(field2, field2.getModifiers() & ~Modifier.FINAL);field2.set(tempCar, 7777);
method.invoke(tempCar);

我们来重点关注其中的操作,我们首先获取 carLength 的 Field 对象,并设置其为可读写的权限。

其次获取该对象的 modifiers 对象,它表示 carLength 被哪些修饰符所修饰。

重点是modifiers.setInt(field2, field2.getModifiers() & ~Modifier.FINAL) 我们逐步进行解析:

  1. modifiers.setInt 对 modifiers 对象进行修改,也就是修改 carLength 的修饰符。

  2. 首先传入实例,重点在其参数,这里实际是一个位操作,getmodifiers() 方法会返回当前对象的修饰符组合,它是由 Java Modifier 类中定义的值所组合起来的。见下图。

  1. 那么 ~Modifier.FINAL 中的 ~ 是对该值取反,0x10 转换为二进制为 0001 0000 取反为 1110 1111& 对其进行与操作,那么实际上就是在去除 FINAL 修饰符。

  2. 最后将其结果修改 modifiers 对象,也就是去除了 FINAL 修饰符。

整段代码的执行结果如下。

反射执行命令

在 Java 中,有一个类叫做 java.lang.Runtime ,这个类有一个 exec 方法可以用于执行本地命令。一般情况下我们使用如下的方法执行命令。我们通过Runtime.getRuntime()方法获得实例,并创建新的Process对象,用于执行命令。

Runtime类是Java中的一个特殊类,它负责提供Java应用程序与运行时环境(Java虚拟机)的交互接口。它被设计为单例模式,确保整个应用程序中只有一个Runtime实例。这种设计决定了Runtime类无法被直接实例化。

package org.example;public class ExecuteCommandDemo {public static void main(String[] args){try {// 创建Runtime对象Runtime temp = Runtime.getRuntime();Process process = temp.exec("calc.exe");// 等待命令执行完毕process.waitFor();} catch (Exception e) {e.printStackTrace();}}
}

而我们同样可以通过反射来执行命令。

首先获取Runtime类对象以便后续的反射操作,再从Runtime类中获取getRuntime方法,通过执行getRuntime方法获取实例,再从类中找到 exec 方法,但由于 exec 具有很多重载版本,我们指定使用接收字符串作为参数的方法。最后通过调用 exec 方法,执行命令。

package org.example;
import java.lang.reflect.Method;public class ExecuteCommandDemo {public static void main(String[] args){try {Class <?> reflectExec = Class.forName("java.lang.Runtime");Method getruntimeMethod = reflectExec.getMethod("getRuntime");Object runtimeInstance = getruntimeMethod.invoke(null);Method execMethod = reflectExec.getMethod("exec", String.class);Process process = (Process) execMethod.invoke(runtimeInstance, "calc.exe");// 等待命令执行完毕process.waitFor();} catch (Exception e) {e.printStackTrace();}}
}

Fastjson的危险反序列化

@type 是fastjson中的一个特殊注解,它告诉 fastjson 应该将 JSON 字符串转换成哪个 Java 类。这很容易出现安全问题

我们来看下面这段代码,我们定义了一串json字符串,想要通过@type注解来将json字符串转化为java.lang.Runtime对象,但是 fastjson在 1.2.24 后默认禁用 autoType 的白名单设置,在默认情况下我们不能任意的将json字符串转化为指定的java类。

但通过 ParserConfig.getGlobalInstance().addAccept("java.lang") 我们可以在白名单中添加 java.lang 类。

后续的代码就是通过反序列化将其转换为对象(这里的Object.class是为了接收转换后的任意对象),再强制转换为Runtime对象,转换完成后就和正常调用java.lang.Runtime执行命令相同了。

package org.example;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;public class FastjsonDangerousDeserialization {public static void main(String[] args) throws Exception{String json = "{\"@type\":\"java.lang.Runtime\"}";ParserConfig.getGlobalInstance().addAccept("java.lang");Runtime runtime = (Runtime) JSON.parseObject(json, Object.class);runtime.exec("calc.exe");}
}

文章转载自:ZywOo

原文链接:https://www.cnblogs.com/RichardLuo/p/18287704/Fastjson_2

体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/43673.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java入门-Day7-面对对象基础-构造器

&#xff08;1&#xff09;特点 创建对象时&#xff0c;对象会去调用构造器 &#xff08;2&#xff09;应用场景 创建对象时&#xff0c;同时完成对对象的成员变量&#xff08;属性&#xff09;&#xff0c;的初始化 &#xff08;3&#xff09;注意事项 //Sutdent构造器 注…

Java 幼儿园(20240709)多来源列表映射多实现类

1、功能场景 对接多个外部厂商&#xff0c;比如多个银行的账单获取&#xff0c;需要根据银行信息执行对应的实现类。 2、功能代码 &#xff08;1&#xff09;YAML配置来源列表 bank-source: bank_001, bank_002, bank_003 &#xff08;2&#xff09;外部来源枚举 public …

《梦醒蝶飞:释放Excel函数与公式的力量》10.2 COMPLEX函数

第二节 10.2 COMPLEX函数 10.2.1函数简介 COMPLEX函数是Excel中的一个工程函数&#xff0c;用于将实部和虚部组合成一个复数。复数广泛应用于工程、电气、物理等领域&#xff0c;COMPLEX函数提供了方便的复数表示和计算方法。 10.2.2语法&#xff1a; COMPLEX(real_num, i_…

flex 填满剩余空间

常见的flex属性值&#xff1a; 1.flex:1,也就是flex-grow:1,也就是上面说的自动放大填充满剩余空间&#xff0c;若有其他子盒子设置flex&#xff0c;则平分。 2.flex:0 0 auto,等同于flex:none,子元素的长度决定它的长度&#xff0c;当整体空间不足时&#xff0c;它也不会缩小&…

swift获取app网络和本地网络权限

请求蓝牙权限&#xff1a; //蓝牙if #available(iOS 13.1, *) {let autostate CBManager.authorizationif(autostate .notDetermined){print("")self.manager CBCentralManager(delegate: nil, queue: DispatchQueue.main,options: [CBCentralManagerOptionShowPo…

【LabVIEW学习篇 - 5】:数据类型——数值、字符串

文章目录 数值枚举下拉列表控件 字符串字符串与十六进制截取字符串连接字符串 字符串与数值间的转换字符串转为数值数值转为字符串 数值 如下图所示&#xff0c;各种数值型数据的不同之处在于存储和表示数据时所使用的位置不同。 浮点型 整型 在LabVIEW中&#xff0c;想要改…

JavaScript中的LHS和RHS

LHS和RHS之前我们先来回忆一下最简单的赋值操作! var test100; console.log(test); 以上代码的意思简单我们理解为把右边的值赋值给左边的test变量,然后输出打印结果。 可是我们要是深入理解你就会发现在这个过程当中&#xff0c;还发生了一些其他的事情 而这些事情就是今天…

C语言 printf函数缓冲机制

printf不立即打印到stdout的原因 printf函数使用了缓冲机制。当我们调用printf时,输出通常不会立即显示在屏幕上,而是先存储在一个缓冲区中。这是为了提高I/O操作的效率。 缓存数据输出的原理 stdio库维护了一个缓冲区。当缓冲区满了,或者在特定条件下,缓冲区的内容会被刷新…

前端如何让网页页面完美适配不同大小和分辨率屏幕

推荐使用postcss插件&#xff0c;它会自动将项目所有的px单位统一转换为vw等单位&#xff08;包括npm安装的第三方组件&#xff09;&#xff0c;从而实现适配&#xff0c;具体配置规则可参考官网或npm网站介绍。 另外对于大屏的适配&#xff0c;需要缩放网页&#xff0c;可使用…

欧姆龙安全PLC及周边产品要点指南

电气安全、自动化设备作业安全&#xff0c;向来是非常非常之重要的&#xff01;越来越多的客户在规划新产线、改造既有产线的过程中&#xff0c;明确要求设计方和施工方将安全考虑进整体方案中进行考虑和报价&#xff01;作为一名自动化电气工程师&#xff0c;尤其是高级工程师…

养宠经验分享猫咪经常掉毛怎么办?最值得买的宠物空气净化器分享

身为资深铲屎官&#xff0c;深知若偷懒不打扫&#xff0c;家中便成猫毛纷飞、异味缭绕的战场&#xff0c;尤其换季时&#xff0c;更是雪上加霜。长期处于这样的环境&#xff0c;不仅我们头疼眼涩、咳嗽气喘&#xff0c;对老人、小孩、孕妇等敏感群体更是健康大敌。 幸运的是&a…

Vagrant配合VirtualBox搭建虚拟机

目录 前言一、软件下载及安装1.下载2.安装扩展&#xff1a; 二、创建一个虚拟机1.Vagrant官方镜像仓库 三、使用远程工具连接虚拟机1.修改相关配置文件 四、虚拟机克隆及使用1.通用配置2.简单搭建一个java环境3.克隆虚拟机1.重命名虚拟机&#xff08;可选&#xff09;2.打包指定…

靶场练习 手把手教你通关DC系列 DC1

DC1靶场通关教程 文章目录 DC1靶场通关教程前言一、信息收集1.主机存活2.端口收集3.网页信息收集4.目录收集4.1 Nikto4.2 Dirb 信息收集总结 二、漏洞发现与利用1. 发现2. 利用 三、FlagFlag1Flag2Flag3Flag4Flag5(提权) 前言 本次使用的kali机的IP地址为192.168.243.131 DC1的…

机器学习 - 比较检验

列联表 列联表&#xff08;Contingency Table&#xff09;是一种用于显示两个或多个分类变量之间关系的表格。它广泛应用于统计学中的分类数据分析&#xff0c;尤其在独立性检验和关联性分析时。列联表的每个单元格展示了相应分类变量组合的频数&#xff08;或比例&#xff09…

【2024_CUMCM】LINGO入门+动态规划

目录 什么是动态规划 怎么使用动态规划&#xff1f; 例题&#xff1a;最短路线问题 2020b-问题一 稳定性分析 灵敏度分析 什么是动态规划 基本想法&#xff1a;将原问题转换为一系列相互联系的子问题&#xff0c;然后通过逐层递推求得最后的解 基本思想&#xff1a;解决…

X12端口配置指南:ISA ID、测试指示符与997

通过知行之桥EDI系统实现X12 & 标准XML之间的格式转换时&#xff0c;需要完善交换头ISA ID及其限定符、测试标识符以及997的相关配置。 在X12文件中有两组EDI ID对&#xff0c;分别是发送方 ID 限定符 及发送方ID &#xff0c;接收方 ID 限定符及接收方ID。 比如&#xf…

STM32Cubemx配置生成 Keil AC6支持代码

文章目录 一、前言二、AC 6配置2.1 ARM ComPiler 选择AC62.2 AC6 UTF-8的编译命令会报错 三、STM32Cubemx 配置3.1 找到stm32cubemx的模板位置3.2 替换文件内核文件3.3 修改 cmsis_os.c文件3.4 修改本地 四、编译对比 一、前言 使用keil ARM compiler V5的时候&#xff0c;编译…

RK3568 buildroot 使用dropbear实现ssh远程的方法

RK3568 buildroot 使用dropbear实现ssh远程的方法 文章目录 RK3568 buildroot 使用dropbear实现ssh远程的方法前言一、创建S99dropbear.sh脚本二、创建sshd_config三、添加root账户密码到系统验证登录前言 rk3568 linux 的sdk中,buildroot已经集成了dropbear的所需的lib库环境…

交替打印-GO

1 两个channel 版本 package mainimport ("fmt""sync")var wg sync.WaitGroup var c1 chan int var c2 chan intfunc A(){defer wg.Done()for i:0;i<10;i {<-c1fmt.Println(2*i)c2<-1 //牵引协程} } func B(){defer wg.Done()for i:0…

Java内存区域与内存溢出异常(自动内存管理)

序言&#xff1a;Java与C之间有一堵由内存动态分配和垃圾收集技术所围成的高墙&#xff0c;墙外面的人想进去&#xff0c;墙里面的人却想出来。 1.1概述 对于从事C、C程序开发的开发人员来说&#xff0c;在内存管理领域&#xff0c;他们既是拥有最高权力的“皇帝”&#xff0c…