前言
概念介绍
接口 : java接口就是协议 像我们的 usb接口 就是为了统一外部硬件的调用规格
api :简单说就是java应用程序的调用 不同的api绑定的是不同的应用程序
反射机制 : Java的反射机制 简单的来说就是 我们调用第三方的jar库或者jdk自带的类时我们可以调用其中的方法和修改一些变量属性 (简单说就是 调用)
编译机制 :
静态编译 : 在编译时确定好类型,绑定对象。
动态编译 : 在运行时确定类型,绑定对象。
反射的应用场景就是在 动态编译 我们调用其他的类的时候 不改变原有的逻辑 变量值 在运行的时候 进行新的属性的赋值修改 从而让调用符合我们的需求
安全场景 :
java反序列化
构造利用链触发 rce
动态的获取或者执行 任意类的属性和方法
反射获取成员变量
先创建项目
创建一个 Admin 类
package com.example.reflectdemo;
public class Admin {
//成员变量
public String name="xiaodi";
public String password="xiaodisec";
//不同修饰符
private int age=18;
//成员方法 Method 成员方法 : 就是你自己写的 方法(当然这个方法你是可以绑定api 然后发给别人 别人是直接可以调用你的这个应用程序的)
// 有参数的
//还有就是成员方法必须是有返回值的 我们使用 void 就是
public void echoname(String name){
this.name=name;
System.out.println(name);
}
//无参数的
public void shuchu(){
System.out.println("输出");
}
//不同修饰符的
private void siyou(){
System.out.println("我是私有变量");
}
// 构造方法 Constructor : 他是指的是我们创建的名字是和我们的主类是相同的 所以这个和成员方法的区别就是名称
public void Admin(String name,String password){
this.name=name;
this.password=password;
System.out.println(name);
System.out.println(password);
}
//不同修饰符号
private void Admin(){
System.out.println("私有的Constructor");
}
}
然后创建一个
开始获取成员变量
获取之前呢 我们需要先获取 这个程序的类
反射获取类名
//根据路径获取
Class class1=Class.forName("com.example.reflectdemo.User");
System.out.println(class1);
上边这个的原理就是根据 forname 根据引用路径获取
//根据对象获取
User users=new User();
Class class3=users.getClass();
System.out.println(class3);
这个是根据新创建了一个对象然后根据对象 获取类 再输出
//根据名字获取
Class aclasss=User.class;
System.out.println(aclasss);
//这个的原理就是根据类的名字直接获取 这个类
反射成员变量并赋值
先获取所有的成员变量 获取成员的变量用到了 Filed
//获取成员变量之前先获取他的 类
User user1 = new User();
Class class1 = user1.getClass();
System.out.println(class1.getName());
// 获取所有的 类
Field[] fields = class1.getDeclaredFields();// [] 表示以数组的形式进行存储
for (Field field : fields) {
System.out.println(field.getName());
}
这个
getDeclaredFields(); 加Declaredfields(); 就是表示 所以的变量不分修饰符 如果没有这个De 的话我们是无权获取的
// 获取单个的变量
//获取单个公共变量
Field fd=class1.getField("name");
System.out.println(fd.getName());
如果使用上边的代码获取 私有的变量时
加上
Declared
就可以了
给与变量赋值 这个就是反射的应用 表示我们对变量进行第三方引用(api调用)的时候 会出现 我们给变量初始化上我们想要的值
获取构造方法
//获取类
User u=new User();
Class c=u.getClass();
//获取全部的构造方法
Constructor[] constructors=c.getDeclaredConstructors();
System.out.println(constructors);
for (Constructor cons:constructors) {//利用数组的遍历 使用
System.out.println(cons);
发现特点就是 构造方法的名字虽然是一样的但是他传入的参数是不同的 这个是可以判断他的用法的
// 获取单个 公共属性的构造方法
Constructor c1=c.getConstructor(String.class);
System.out.println(c1);
// 获取单个全部属性的构造方法
Constructor c1=c.getDeclaredConstructor(String.class,int.class);
System.out.println(c1);
获取成员方法 和 成员方法的调用
先说一个 继承(代码的复用和精简)
继承就是指 一个大类中逐渐分化出特点功能的方法(这类方法的调用得 person.say 这样的形式进行调用)
成员方法也是有继承的
//获取继承的Method 类
Method[] idea=aclass.getMethods();
for (Method m:idea){
System.out.println(m);
}
//获取非继承的Method 类
Method[] idea=aclass.getDeclaredMethods();
for (Method m:idea){
System.out.println(m);
}
//获取单个
Method m=aclass.getMethod("userinfo", String.class, int.class, String.class, String.class);
System.out.println(m);
// //第三方 调用运行方法
User u=new User();
Method fangfa2=aclass.getMethod("userinfo", String.class, int.class, String.class, String.class);
fangfa2.invoke(u,"xiaodigay",18,"xx","xx");
安全问题
指应用程序使用具有反射功能的外部输入来选择要使用的类或代码,
可能被攻击者利用而输入或选择不正确的类。绕过身份验证或访问控制检查
参考分析:https://zhuanlan.zhihu.com/p/165273855
利用结合:https://xz.aliyun.com/t/7031(反序列化利用链)
小案例 :
利用反射实现RCE(这个RCE的功能是 我们要调用的aip的一个子接口)
我们ctrl + 鼠标左键 直接追踪
getRuntime是一个 public类的一个成员方法
exec也是
但是上边这个get他的返回值是一个实例化对象 我们知道要想让方法被调用就得使用实例化的对象进行调用
这个的思路我们就想到获取 getRuntime的方法 然后我们直接把他当做对象 来实例化第三方调用这个api exec
思路有了开造
//第三方的调用
//先获取类名
Class aaa=Runtime.class;
// System.out.println(aaa);
// 获取所以的方法接口
Method[] methods=aaa.getMethods();
for (Method method:methods) {
System.out.println(method);
}
获取这个干鸡毛啊 : 为了更方便的获取单个的方法
Class aaa=Runtime.class;
// System.out.println(aaa);
Method[] methods=aaa.getMethods();
for (Method method:methods) {
System.out.println(method);
}
Method m=aaa.getMethod("getRuntime");
Method exec=aaa.getMethod("exec", String.class);
Object o=m.invoke(aaa);
exec.invoke(o,"calc"); //exec 表示方法
// invoke 的执行是需要对象的
直接执行 就可以弹计算机了