Java学习之反射篇
Java学习之反射篇
0x00 前言
今天简单来记录一下,反射与注解的一些东西,反射这个机制对于后面的java反序列化漏洞研究和代码审计也是比较重要。
0x01 反射机制概述
Java反射是Java非常重要的动态特性,通过使用反射我们不仅可以获取到任何类的成员方法、成员变量、构造方法等信息,还可以动态创建Java类实例、调用任意的类方法、修改任意的类成员变量值等。Java反射机制是Java语言的动态性的重要体现,也是Java的各种框架底层实现的灵魂。
0x02 Java反射
Java反射操作的是java.lang.Class对象,所以我们需要要先获取到Class对象。
获取Class对象的方式:
1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
多用于配置文件,将类名定义在配置文件中。读取文件,加载类
2. 类名.class:通过类名的属性class获取
多用于参数的传递
3. 对象.getClass():getClass()方法在Object类中定义着。
多用于对象的获取字节码的方式
代码实例:
方式一:
Class cls1 = Class.forName("Domain.Person");
System.out.println(cls1);
方式二:
Class cls2 = Person.class;
System.out.println(cls2);
方式三:
Person p = new Person();
Class cls3 = p.getClass();
System.out.println(cls3);
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
class类方法:
获取成员变量方法:
1. 获取成员变量们
* Field[] getFields() :获取所有public修饰的成员变量
* Field getField(String name) 获取指定名称的 public修饰的成员变量
* Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
* Field getDeclaredField(String name)
获取构造方法:
* Constructor<?>[] getConstructors()
* Constructor<T> getConstructor(类<?>... parameterTypes)
* Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
* Constructor<?>[] getDeclaredConstructors()
获取成员方法:
* Method[] getMethods()
* Method getMethod(String name, 类<?>... parameterTypes)
* Method[] getDeclaredMethods()
* Method getDeclaredMethod(String name, 类<?>... parameterTypes)
获取全类名:
String getName()
成员变量设置:
Field:成员变量
* 操作:
1. 设置值
* void set(Object obj, Object value)
2. 获取值
* get(Object obj)
3. 忽略访问权限修饰符的安全检查
* setAccessible(true):暴力反射
构造方法:
创建对象:
* T newInstance(Object... initargs)
* 如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法
方法对象:
执行方法:
* Object invoke(Object obj, Object... args)
* 获取方法名称:
* String getName:获取方法名
使用getField方法获取成员变量
我们现在这里编写一个person类。
person代码:
package Domain;
public class Person {
private String name ;
private int age;
public String a ;
public Person() {
}
public void eat(){
System.out.println("eat");
}
public void eat(String food){
System.out.println("eat "+food);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", a='" + a + '\'' +
'}';
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
main类代码:
public static void main(String[] args) throws Exception {
Class cls = Class.forName("Domain.Person");
Field a = cls.getField("a"); //获取成员a变量
Person person = new Person();
Object o = a.get(person); //获取成员变量的值
System.out.println(o);
a.set(person,"abc"); //修改成员a变量的值为abc
System.out.println(person);
}
使用getDeclaredFields获取所有成员变量
该方法不考虑修饰符
public static void main(String[] args) throws Exception {
Class cls = Class.forName("Domain.Person");
System.out.println(person);
Field[] declaredFields = cls.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
使用getDeclaredField获取指定成员变量
Class cls = Class.forName("Domain.Person");
Field b = cls.getDeclaredField("b");
b.setAccessible(true); //使用暴力反射机制,忽略访问权限修饰符的安全检测
Person person = new Person();
Object o1 = b.get(person);
System.out.println(o1);
这里person该类中的成员变量是被private修饰的,我们想要访问他的值必须使用暴力反射,暴力反射
可以,忽略访问权限修饰符的安全检测。
获取构造方法
Class cls = Class.forName("Domain.Person");
Constructor constructor = cls.getConstructor(String.class,int.class);//获取构造器
System.out.println(constructor);
//有参构造
Object o = constructor.newInstance("123", 18); //创建对象
System.out.println(o);
//无参构造
Object o1 = constructor.newInstance();
System.out.println(o1);
获取方法
Class cls = Class.forName("Domain.Person");
//无参数方法
Method eat = cls.getMethod("eat");
Person person = new Person();
eat.invoke(person); //调用eat方法
//有参数方法
Method eat1 = cls.getMethod("eat", String.class); //获取eat方法并且设置参数
eat1.invoke(person,"fish");
获取所有public修饰方法
Class cls = Class.forName("Domain.Person");
Method[] methods = cls.getMethods();
for (Method method : methods) {
System.out.println(method);
}
获取类名
Class cls = Class.forName("Domain.Person");
String name = cls.getName();
System.out.println(name);
前面这些只是简单的一些方法的使用,后面来看一个小案例来了解反射的具体应用。
步骤
1.首先我们需要创建一个配置文件,然后定义需要创建的兑现和需要执行的方法定义在配置文件里面。
2.在程序中读取配置文件
3.使用反射机制加载类文件进内存
4.创建对象
5.执行方法
一般java里面的配置文件都是以.properites结尾,那么就定义一个pro.properites文件。
pro.properites文件内容:
className=Domain.Person //写入需要加载的类
methodName=eat //写入需要加载的方法
main类里面内容:
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
Properties properties = new Properties(); //创建properties对象
ClassLoader classLoader = Person.class.getClassLoader(); //获取加载
InputStream resourceAsStream = classLoader.getResourceAsStream("pro.properites"); //获取路径文件流
properties.load(resourceAsStream); //加载文件
//获取配置文件定义的数据
String className = properties.getProperty("className"); //获取类名
String methodName = properties.getProperty("methodName");//获取方法名
Class cls = Class.forName(className); //将类加载进内存
Object o = cls.newInstance(); //创建无参构造对象
Method method = cls.getMethod(methodName); //创建方法
method.invoke(o); //调用方法
}
}
如果我们需要修改调用的方法或者说类,可以直接在配置文件里面进行修改,无需修改代码。
0x03 反射调用Runtime
Runtime这个函数有exec方法可以本地执行命令,大部分关于jsp命令执行的payload可能都是调用Runtime进行Runtime的exec方法进行命令执行的。
不利用反射执行命令
package com;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
public class Test {
public static void main(String[] args) throws IOException {
InputStream ipconfig = Runtime.getRuntime().exec("ipconfig").getInputStream();
String s = IOUtils.toString(ipconfig,"gbk"); //使用IOUtils.toString静态方法将字节输入流转换为字符
System.out.println(s);
}
}
这样的代码基本都是固定死的,如果要多次传入参数执行命令的话,这样的写法肯定是不行的,那么这时候就可以用到反射。
package com;
import org.apache.commons.io.IOUtils;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class Test2 {
public static void main(String[] args) throws Exception {
String command = "ipconfig";
Class cls = Class.forName("java.lang.Runtime"); //Runtime加载进内存
Constructor declaredConstructor = cls.getDeclaredConstructor(); //获取构造方法
declaredConstructor.setAccessible(true); //暴力反射
Object o = declaredConstructor.newInstance(); //创建Runtime类
Method exec = cls.getMethod("exec", String.class); //获取exec方法,设置需要参数string类型参数
Process process = (Process) exec.invoke(o,command); //执行exec方法,并传入ipconfig参数
// System.out.println(process);
InputStream inputStream = process.getInputStream(); //获取输出的数据
String ipconfig = IOUtils.toString(inputStream,"gbk"); //字节输出流转换为字符
System.out.println(ipconfig);
}
}
这时候只需要修改command的值,无需修改代码就可以执行其他的命令了。

0x04 结尾
一边调试代码,一边码文章,写完不知不觉已经5点了。还是洗洗谁吧。

Java学习之反射篇的更多相关文章
- Java学习之注解篇
Java学习之注解篇 0x00 前言 续上篇文章,这篇文章就来写一下注解的相关内容. 0x01 注解概述 Java注解(Annotation)又称Java标注,是JDK5.0约会的一种注释机制. 和J ...
- java学习之反射机制
java语言区别于C,C++等准静态语言的最大特点就是java的反射机制.静态语言的最直接定义就是不能在运行时改变程序结构或变量的类型.按照这样的定义,python,ruby是动态语言,C,C++,J ...
- 从.Net到Java学习第十一篇——SpringBoot登录实现
从.Net到Java学习系列目录 通过前面10篇文章的学习,相信我们对SpringBoot已经有了一些了解,那么如何来验证我们的学习成果呢?当然是通过做项目来证明啦!所以从这一篇开始我将会对之前自己做 ...
- Java学习之jackson篇
Java学习之jackson篇 0x00 前言 本篇内容比较简单,简单记录. 0x01 Json 概述 概述:JSON(JavaScript Object Notation, JS 对象简谱) 是一种 ...
- java学习之 反射
以前学习java只是学习了基本语法操作,各种常用方法的使用,随着慢慢学习,很多大神都觉得要想成为大神,就必须把java的反射给理解透,这样我就带着好奇的心去学习到底反射是什么玩意,所以就上网找资料学习 ...
- 从.Net到Java学习第八篇——SpringBoot实现session共享和国际化
从.Net到Java学习系列目录 SpringBoot Session共享 修改pom.xml添加依赖 <!--spring session--> <dependency> & ...
- 从.Net到Java学习第四篇——spring boot+redis
从.Net到Java学习系列目录 “学习java已经十天,有时也怀念当初.net的经典,让这语言将你我相连,怀念你......”接上一篇,本篇使用到的框架redis.FastJSON. 环境准备 安装 ...
- 从.Net到Java学习第三篇——spring boot+mybatis+mysql
从.Net到Java学习第一篇——开篇 环境:mysql5.7 新建mysql数据库demo,然后执行如下sql脚本进行数据表创建和数据初始化: -- ------------------------ ...
- Java 学习笔记提高篇
Java笔记(提高篇)整理 主要内容: 面向对象 异常 数组 常用类 集合 IO流 线程 反射 Socket编程 1. 面向对象 1.1包 用来管理Java中的类, 类似文件夹管理文件一样. 因 ...
随机推荐
- 【C#】根据开始时间和结束时间筛选存在的信息
背景 业务需求中,需要根绝开始时间和结束时间筛选一段时间内的任务存在个数. 示例图片 根据开始时间 9:00到 结束时间11:00 筛选信息 总共有这么四种情况可能出现 插入测试数据 CREATE T ...
- git的几个常用基本操作
需求一:如何把stage中的修改还原到work dir中 这个需求很常见,也很重要,比如我先将当前work dir中的修改添加到stage中,然后又对work dir中的文件进行了修改,但是又后悔了, ...
- 最小割树(Gomory-Hu Tree)
当我们遇到这样的问题: 给定一个 \(n\) 个点 \(m\) 条边的无向连通图,多次询问两点之间的最小割 我们通常要用到最小割树. 博客 建树 分治.记录当前点集,然后随便找俩点当 \(s\) 和 ...
- xctf-pwn hello_pwn
走流程,看看文件类型 64位,开了NX 直接丢IDA分析 查看sub_400686() 是个给flag的函数,可以看到,只要满足if语句的条件使dword_60106C == 1853186401就可 ...
- python关于字符编码的基本操作
字符编码 (注意:关于字符编码,如果没有特殊业务要求,请牢记仅使用UTF-8编码) 由于Python的字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节.如果要在网络上传输,或者 ...
- Python环境那点儿事(Windows篇)
Python环境配置那点儿事(Windows篇) 版本选择 (根据你的开发经验选择合适版) 适当版2.7 适当版3.6 适当版3.7 下载链接:python.org 安装 正规的Windows10操作 ...
- python unicode和string byte
python unicode 和string那 开发过程中总是会碰到string, unicode, ASCII, 中文字符等编码的问题, 每次碰到都要现搜, 很是浪费时间, 于是这次狠下心, 一定要 ...
- Redis之Redis的数据类型
1.Redis的数据类型 Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(无序集合)及ZSet(有序集合) 2.String(字符串) ...
- 让你的GitHub下载飞速提升到2M/s以上
2020年7月27日整理发布多种GitHub加速方式! 转载自:https://code.pingbook.top/blog/2020/How-To-Speed-Github.html 1. GitH ...
- 算法数据结构 | 只要30行代码,实现快速匹配字符串的KMP算法
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是算法数据结构专题的第29篇文章,我们来聊一个新的字符串匹配算法--KMP. KMP这个名字不是视频播放器,更不是看毛片,它其实是由Kn ...