IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> Javaweb安全学习——Java反射 -> 正文阅读

[网络协议]Javaweb安全学习——Java反射

Java反射机制

Java反射(Reflection)是Java非常重要的动态特性,通过使用反射我们不仅可以获取到任何类的成员方法(Methods)、成员变量(Fields)、构造方法(Constructors)等信息,还可以动态创建Java类实例、调用任意的类方法、修改任意的类成员变量值等。Java反射机制是Java语言的动态性的重要体现,也是Java的各种框架底层实现的灵魂。

获取Class对象

Java反射操作的是java.lang.Class对象,所以我们需要先想办法获取到Class对象,通常我们有如下几种方式获取一个类的Class对象:

  1. Class.forName("ClassName");
  2. ClassLoader.loadClass("ClassName"); //实际使用时需要指定具体的加载器如ClassLoader.getSystemClassLoader(),再调用loadClass方法
  3. ClassName.class; //类已经被加载,只是获取其java.lang.Class对象
  4. obj.getClass(); //上下文中存在某个类的实例obj时可用

Class.forName(String)是最常用的的获取Class方式,forname()有两个方法重载:

  • public static Class<?> forName(String className)
    
  • public static Class<?> forName(String name,
                   boolean initialize,
                   ClassLoader loader)
    

第一种方法可以理解为第二种方法的一个封装,调用此方法等效于第二种。

Class.forName("Foo")

相当于:

Class.forName("Foo", true, this.getClass().getClassLoader())

值得注意的是第二种方法的第二、三个参数,第二个参数表示是否初始化,第三个参数是ClassLoader(在上一节内容中所提到的)。

第二个参数initialize指的初始化可以理解为类的初始化,在上一节当中Class.forname()的例子中也提到了在类初始化static{}语句块被调用(编写恶意类,将恶意代码放入static语句块即可加载),p神在Java安全漫谈当中给出的例子展现了三个“初始化”方法之间的区别,以及调用顺序。

在这里插入图片描述

另一个方法ClassLoader.loadClass(String)也有一个方法重载和上面一样

Class<?> loadClass(String name)   //调用此方法等效于调用loadClass(name, false)
Class<?> loadClass(String name,
                 boolean resolve)

第一个 ( Class.forName();) 将:

  • 使用加载调用此代码的类的类加载器
  • 初始化类(也就是说,所有静态初始化程序都将运行)

另一个 ( ClassLoader.getSystemClassLoader().loadClass();) 将:

  • 使用“系统”类加载器(可覆盖
  • 不初始化类

实例——获取Runtime类Class对象代码片段:

String className     = "java.lang.Runtime";
Class  runtimeClass1 = Class.forName(className);
Class  runtimeClass2 = ClassLoader.getSystemClassLoader().loadClass(className);
Class  runtimeClass3 = java.lang.Runtime.class;

获取内部类

反射调用内部类的时候需要使用$来代替.,如Common$Inner,通过Class.forname("Common$Inner");加载

class Common {
    static {
        System.out.printf("CommonClass: %s\n", Common.class);
    }
    class Inner{
        Inner(){
            System.out.printf("InnerClass: %s\n", Inner.class);}}}

在编译的时候会成两个文件Common$Inner.classCommon.class就像两个类一样。

铺垫完了,下面进入正题。

使用反射生成并操作对象

Class对象可以获得该类里面的:

  • 方法(由Method对象表示),通过Method对象执行方法;
  • 构造器(由Constructor对象表示),通过Constructor对象来调用构造函数;
  • 成员变量值(由Field对象表示),通过Field对象直接访问并修改对象的成员变量值;

创建对象(类实例)

需要先使用Class对象获取指定的构造方法(Constructor对象),再调用Constructor对象的newInstance()方法(作用是调用获取到的无参构造方法)来创建该Class对象对应类的实例。

Class类当中大体有两种获取构造函数的方法:

getConstructor*()方法,只能获取公有方法。

* Constructor<?>[] getConstructors()
* Constructor<T> getConstructor(<?>... parameterTypes)

getDeclaredConstructor*()方法,可获取到类的私有构造器(包括带有其他修饰符的构造器),须设置setAccessible()为true。

* Constructor<?>[] getDeclaredConstructors()
* Constructor<T> getDeclaredConstructor(<?>... parameterTypes)

调用方法

获得某个类的实例(Class对象)之后,通过该Class对象的getMethod*()getDeclaredMethod*()来获取方法。

getMethod*()方法,获取类的所有共有方法,包括自身、从基类继承的、从接口实现的所有public方法。

Method[] getMethods()  //全部方法 返回Method对象
Method getMethod(String name,<?>... parameterTypes)   //指定方法 返回Method数组

getDeclaredMethod*()方法,可获取类自身声明的所有方法,包含public、protected和private方法。

 Method[] getDeclaredMethods()
 Method getDeclaredMethod(String name,<?>... parameterTypes)

接着在获得Method对象之后,可通过该Method对象的invoke()方法来调用其对应方法。

Object invoke(Object obj, Object... args) 
    obj - 被调用方法的类实例对象(静态方法则为类)
	args - 用于方法调用的参数类型列表(Java存在方法重载,以此确定要调用的方法)

在调用对象的private方法时,需要先使用Methon.setAccessible(true)设置为忽略访问权限的限制。

调用成员变量

通过Class对象的getField*()getDeclaredField*()方法可以获取该类所包含的指定成员变量。

getField*()只能访问public修饰的变量。

Field[] getFields() //全部变量
Field getField(String name)  //指定变量

getDeclaredField*()可以随意的访问指定对象的所有成员变量,包括private成员变量。

Field[] getDeclaredFields()
Field getDeclaredField(String name)

获取成员变量值:

Object obj = field.get(类实例对象);

修改成员变量值:

field.set(类实例对象, 修改后的值);

在操作私有变量时,field.setAccessible(true)即可忽略访问成员变量访问权限限制。

修改被final关键字修饰的成员变量,需先修改set方法。

// 反射获取Field类的modifiers
Field modifiers = field.getClass().getDeclaredField("modifiers");

// 设置modifiers修改权限
modifiers.setAccessible(true);

// 修改成员变量的Field对象的modifiers值
modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);

// 修改成员变量值
field.set(类实例对象, 修改后的值);

上面三个部分都提到了调用setAccessible()方法去解决修饰符的权限问题,该方法不属于这三者的某一类,setAccessible() 属于Field、Method 和 Constructor 对象的父类AccessibleObject 。它提供了在使用反射对象时将其标记为抑制默认 Java 语言访问控制检查的能力,其override属性默认为false,可调用setAccessible()方法改变。因此Field、Method 、Constructor 都可调用此方法。

实例——反射java.lang.Runtime命令执行

// 获取Runtime类对象
Class runtimeClazz = Class.forName("java.lang.Runtime");

// 获取Runtime类的无参构造方法(该方法为私有方法)
Constructor constructor = runtimeClazz.getDeclaredConstructor();
//修改方法的访问权限(constructor.setAccessible(true))
constructor.setAccessible(true);

// 创建Runtime类实例,等价于 Runtime rt = new Runtime();
Object runtimeInstance = constructor.newInstance();

// 获取Runtime的exec(String cmd)方法
Method runtimeMethod = runtimeClazz.getMethod("exec", String.class);

// 调用exec方法,等价于 rt.exec(cmd);
runtimeMethod.invoke(runtimeInstance, "calc.exe");

Runtime是在写命令执行payload时最常用的类,Runtime类是单例模式的。在这里插入图片描述

构造方法是私有的,但给了一个静态方法getRuntime()来获取Runtime对象,所以还可以使用如下payload

Class clazz = Class.forName("java.lang.Runtime");
Method execMethod = clazz.getMethod("exec", String.class);
Method getRuntimeMethod = clazz.getMethod("getRuntime");
Object runtime = getRuntimeMethod.invoke(clazz);
execMethod.invoke(runtime, "calc.exe");

Java反射机制总结

Java反射机制是Java动态性中最为重要的体现,利用反射机制我们可以轻松的实现Java类的动态调用。Java的大部分框架都是采用了反射机制来实现的(如:Spring MVCORM框架等),Java反射在编写漏洞利用代码、代码审计、绕过RASP方法限制等中起到了至关重要的作用。
thod.invoke(clazz);
execMethod.invoke(runtime, “calc.exe”);


## Java反射机制总结

Java反射机制是Java动态性中最为重要的体现,利用反射机制我们可以轻松的实现Java类的动态调用。Java的大部分框架都是采用了反射机制来实现的(如:`Spring MVC`、`ORM框架`等),Java反射在编写漏洞利用代码、代码审计、绕过RASP方法限制等中起到了至关重要的作用。
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-04-04 12:47:24  更:2022-04-04 12:49:59 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 4:24:31-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码