-
Java
安全大部分都是从反射开始,可以动态调用一些Payload而不出现相关特征。
类初始化代码执行顺序
- 首先
new
一个类,加载的顺序是(父)静态变量->(父)静态代码块->(父)代码块->(父)构造方法。 - 如果初始化同一个类,静态变量、静态代码块只执行一次。
package Advance;
public class ExecutionOrder {
public static void main(String[] args) {
Student student = new Student();
}
}
class Student extends Person {
public Student() {
System.out.println("学生构造函数");
}
{
System.out.println("学生代码块");
}
static {
System.out.println("学生静态代码块");
}
}
class Person {
public static String staticName = "Pan3a";
public String name = "Forever";
{
System.out.println("人类代码块");
}
static {
System.out.println("人类静态代码块");
}
public Person() {
System.out.println("人类构造函数");
}
}
- 输出结果
人类静态代码块
学生静态代码块
人类代码块
人类构造函数
学生代码块
学生构造函数
类加载器
- 这里暂时不去深究吧,后面学习参考文章
- 类加载器和Class.forName区别
- https://javasec.org/javase/ClassLoader/
环境
package Reflection;
public class ReflectData {
public ReflectData() {
}
}
class Person {
private int age;
private String name;
public long id = 9527;
public long grade;
protected float score;
protected int rank;
public Person() {
}
protected Person(long id) {
this(18, "Pan3a", id, 9, 9999, 31);
}
private Person(int age) {
this(age, "Pan3a", 9527, 9, 9999, 30);
}
public Person(int age, String name, long id, long grade, float score, int rank) {
this.age = age;
this.name = name;
this.id = id;
this.grade = grade;
this.score = score;
this.rank = rank;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public long getGrade() {
return grade;
}
public void setGrade(long grade) {
this.grade = grade;
}
public float getScore() {
return score;
}
public void setScore(float score) {
this.score = score;
}
public int getRank() {
return rank;
}
public void setRank(int rank) {
this.rank = rank;
}
private static void sayHello() {
System.out.println("Hello World");
}
private void sayHello(String name) {
System.out.println("Hello " + name);
}
@Override
public String toString() {
final StringBuffer stringBuffer = new StringBuffer("Person{");
stringBuffer.append("age=").append(age);
stringBuffer.append(", name='").append(name).append('\'');
stringBuffer.append(", id=").append(id);
stringBuffer.append(", grade=").append(grade);
stringBuffer.append(", score=").append(score);
stringBuffer.append(", rank=").append(rank);
stringBuffer.append('}');
return stringBuffer.toString();
}
}
class Teacher extends Person {
private String role = "Teacher";
public void sayHello() {
System.out.println("Hello Teacher");
}
}
class Student extends Teacher {
private String role = "Student";
static {
System.out.println("run Student static codes!");
}
{
System.out.println("run Student codes!");
}
@Override
public void sayHello() {
System.out.println("Hello Student");
}
public Student(){
}
public Student(String name){
System.out.printf("hello %s", name);
}
}
获取对象
- 获取对象有三种方式,
Class.forName(全类名)
、对象.getclass()
、类名.class
。 - 这里的三种方式虽然作用都一样,但是都有各自的缺点。
class.forName()
需要知道类名的全路径。对象名.getclass()
需要存在已经实例化的对象,类名.class
需要提前在编译前知道类名。 - 这里都返回
true
了,说明这里返回的对象都是同一个,因此Person.class只加载了一次。用instanceof不但匹配指定类型,还匹配指定类型的子类。而用==判断class实例可以精确地判断数据类型,但不能作子类型比较。
package Reflection;
public class ReflectGetObject {
public static void main(String[] args) throws ClassNotFoundException{
compareReflectGetObject();
}
public static void compareReflectGetObject() throws ClassNotFoundException {
Student student = new Student();
Teacher teacher = new Teacher();
Person person = new Person();
if(student instanceof Person){
System.out.println("Student 是Person子类");
}
Class<?> clazz1 = Class.forName("Reflection.Person");
System.out.println(clazz1.getName());
Class<?> clazz2 = person.getClass();
System.out.println(clazz2.getName());
Class<?> clazz3 = Person.class;
System.out.println(clazz3.getName());
System.out.println(clazz1 == clazz2);
System.out.println(clazz1 == clazz3);
}
}
- 输出结果如下
run Student static codes!
run Student codes!
Student 是Person子类
Reflection.Person
Reflection.Person
Reflection.Person
true
true
- 这里的
Class.forName()
有两个函数重载,我们可以跟进看看。第一个函数是我们平常用的,第二个有参数boolean initialize
、ClassLoader loader
,可以指定类是否在加载时初始化,和指定类加载器。 -
重点
,这里的boolean initialize
参数是控制是否初始化
,而不是实例化,初始化会加载静态变量
、静态代码块
。
@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
@CallerSensitive
public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException
{
Class<?> caller = null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// Reflective call to get caller class is only needed if a security manager
// is present. Avoid the overhead of making this call otherwise.
caller = Reflection.getCallerClass();
if (sun.misc.VM.isSystemDomainLoader(loader)) {
ClassLoader ccl = ClassLoader.getClassLoader(caller);
if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
sm.checkPermission(
SecurityConstants.GET_CLASSLOADER_PERMISSION);
}
}
}
return forName0(name, initialize, loader, caller);
}
- 案例如下
package Reflection;
public class ReflectGetObject {
public static void main(String[] args) throws ClassNotFoundException{
classForNameStudy();
}
public static void classForNameStudy() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> clazz = Class.forName("Reflection.Student");
// 这里必须用getDeclaredConstructor()方法,因为这里是自动生成的构造函数不是public修饰的
Constructor<?> constructor = clazz.getDeclaredConstructor();
// 返回int类型的数据,自己去网站找对应的数值,这里返回0
System.out.println(constructor.getModifiers());
System.out.println("------------------");
constructor.setAccessible(true);
Object student = constructor.newInstance();
System.out.println("------------------\n\n");
Class<?> clazz1 = Class.forName("Reflection.Student",false,Thread.currentThread().getContextClassLoader());
Constructor<?> constructor1 = clazz1.getDeclaredConstructor();
System.out.println(constructor1.getModifiers());
System.out.println("------------------");
constructor1.setAccessible(true);
Object student1 = constructor1.newInstance();
System.out.println("------------------");
}
}
- 输出结果如下,这里的静态代码块只执行了一次,后面没执行,是因为
Student.class
已经被前面加载到内存中了,
run Student static codes!
0
------------------
run Student codes!
------------------
0
------------------
run Student codes!
------------------
- 若注释掉第一段
39-47
行,输出结果如下,可以看到静态代码块是在newInstance()
实例化后再加载到,因此可以得出initialize
参数控制的是是否初始化,而不是实例化。
0
------------------
run Student static codes!
run Student codes!
------------------
构造方法
- 有以下四种获取方法。
getConstructor()
、getConstructors()
、getDeclaredConstructor()
、getDeclaredConstructors()
,若构造方法带参数,用对应的数据类型.class
即可。 - 这里注意非public的构造方法需要使用
getDeclaredConstructor()
方法即可,然后如果需要实例化必须使用setAccessible(true)
。
package Reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectGetObject {
public static void main(String[] args) throws Exception{
getConstructorsStudy();
}
public static void getConstructorsStudy() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> clazz = Class.forName("Reflection.Person");
System.out.println("所有构造方法");
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
for (Constructor<?> constructor:constructors){
System.out.println(constructor);
}
System.out.println();
System.out.println("public无参数构造方法");
// 默认当前类的无参数构造方法
Constructor<?> constructor1 = clazz.getConstructor();
System.out.println(constructor1);
System.out.println("protected带参数构造方法");
Constructor<?> constructor2 = clazz.getDeclaredConstructor(long.class);
System.out.println(constructor2);
System.out.println("private带参数构造方法");
Constructor<?> constructor3 = clazz.getDeclaredConstructor(int.class);
System.out.println(constructor3 + "\n");
System.out.println("public无参数构造方法创建对象");
Object person1 = constructor1.newInstance();
System.out.println(person1);
System.out.println("protected带参数构造方法创建对象");
constructor2.setAccessible(true);
Object person2 = constructor2.newInstance(9528);
System.out.println(person2);
System.out.println("private带参数构造方法创建对象");
constructor3.setAccessible(true);
Object person3 = constructor3.newInstance(18);
System.out.println(person3);
System.out.println("Person.class.newInstance()");
Class<?> class1 = Reflection.Person.class;
Object object = class1.newInstance();
System.out.println(object);
}
}
- 输出结果展示
所有构造方法
protected Reflection.Person(long)
private Reflection.Person(int)
public Reflection.Person(int,java.lang.String,long,long,float,int)
public Reflection.Person()
public无参数构造方法
public Reflection.Person()
protected带参数构造方法
protected Reflection.Person(long)
private带参数构造方法
private Reflection.Person(int)
成员方法
- 获取成员方法,也有四个方法。
getMethod()
,getMethods()
,getDeclaredMethod()
,getDeclaredMethods()
,invoke()
,这里如果是继承,invoke()
调用的方法根据实际传入的方法为准,不存在则向上父类寻找。 - 获取方法属性,
getName()
,getReturnType()
,getParameterTypes()
,getModifiers()
。
package Reflection;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
public class ReflectGetObject {
public static void main(String[] args) throws Exception{
getMethodStudy();
}
public static void getMethodStudy() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
Class<?> clazz = Class.forName("Reflection.Person");
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Object person = constructor.newInstance();
Method method = clazz.getMethod("setAge", int.class);
method.invoke(person,18);
// getDeclaredMethods学习
System.out.println("所有方法");
Method[] methods = clazz.getDeclaredMethods();
for (Method methodTemp:methods){
System.out.println(methodTemp.getName() + "\t" + Arrays.toString(methodTemp.getParameterTypes()));
}
// getMethod学习
Method method1 = clazz.getMethod("getAge");
method1.setAccessible(true);
System.out.println(method1.invoke(person));;
// 由于这里和方法为静态方法,因此object为null
Method method2 = clazz.getDeclaredMethod("sayHello");
method2.setAccessible(true);
method2.invoke(null);
// 多态,依旧根据传入的实例化对象为准,如果没有则向父类寻找
Class<?> teacherClass = Class.forName("Reflection.Teacher");
Method method3 = teacherClass.getMethod("sayHello");
method3.invoke(new Reflection.Student());
}
}
- 输出结果
所有方法
setAge [int]
getAge []
sayHello [class java.lang.String]
sayHello []
setId [long]
getGrade []
setGrade [long]
getScore []
setScore [float]
getRank []
setRank [int]
toString []
getName []
setName [class java.lang.String]
getId []
18
Hello World
run Student static codes!
run Student codes!
Hello Student
成员变量
- 获取成员变量需要知道一下四个方法,getField,getFields,getDeclaredField,getDeclaredFields。
- 还可获取成员变量属性的三个方法,getType,getModifiers,getName。分别是获取变量类型,修饰符,成员名。
- 他们分别是获取单个成员,和获取所有成员,获取单个成员(忽略修饰服限制,不包括父类),获取多个成员(忽略修饰服限制,不包括父类)。需注意的是使用后面两个是需使用setAccessible(true)来忽略编译时的安全检查。
package Reflection;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
public class ReflectGetObject {
public static void main(String[] args) throws Exception{
getFieldStudy();
}
public static void getFieldStudy() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
Class<?> clazz = Class.forName("Reflection.Person");
Constructor<?> constructor = clazz.getConstructor();
Object person = constructor.newInstance();
Method setAge = clazz.getMethod("setAge", int.class);
setAge.invoke(person,18);
Method getAge = clazz.getMethod("getAge");
System.out.println(getAge.invoke(person) + "\n");
// getgetDeclaredFields学习
System.out.println("所有方法");
Field[] fields = clazz.getDeclaredFields();
for (Field field:fields){
System.out.println(field.getName() + "\t" + field.getModifiers() + "\t" + field.getType());
}
// 反射修改私有成员值
Field field = clazz.getDeclaredField("age");
field.setAccessible(true);
field.set(person,20);
System.out.println(getAge.invoke(person));
}
}
- 输出结果
18
所有方法
age 2 int
name 2 class java.lang.String
id 1 long
grade 1 long
score 4 float
rank 4 int
20
反射Runtime执行命令
- 代码如下,依次来解释
package Reflection;
import java.lang.reflect.Method;
public class ReflectRuntimeExec {
public static void main(String[] args) throws Exception{
Class<?> clazz = Class.forName("java.lang.Runtime");
Method getRuntime = clazz.getDeclaredMethod("getRuntime");
Object runtime = getRuntime.invoke(null);
Method exec = clazz.getDeclaredMethod("exec", String.class);
exec.invoke(runtime,"open /System/Applications/Calculator.app");
}
}
- 这里为什么要获取两个方法呢,为什么没有实例化类就可以调用方法了。
- 跟进
Runtime
,发现它的构造方法是私有的,发现有一个getRuntime
的静态方法,可以返回实例化的对象,就像P神说的,“单例模式
”因此我们通过获取getRuntime
方法来获取Runtime的实例,还可以反射构造方法来获取,只不过后者比前者麻烦。 -
疑问
,这里为什么不可以获取字段来直接获取变量值。我觉得可能是这里只是获取的属性,并没有获取变量的值吧。这里发现可以通过Filed.get(clazz)
来获取变量的值,问题解决。
getRuntime命令执行
package Reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.Runtime;
public class ReflectRuntimeExec {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("java.lang.Runtime");
Method getRuntime = clazz.getDeclaredMethod("getRuntime");
Method exec = clazz.getDeclaredMethod("exec", String.class);
Object runtime = getRuntime.invoke(null);
exec.invoke(runtime, "open /System/Applications/Calculator.app");
}
}
Constructor命令执行
- 这里没有意外,直接反射操作即可。
package Reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.Runtime;
public class ReflectRuntimeExec {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("java.lang.Runtime");
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Object runtime2 = constructor.newInstance();
System.out.println(runtime2 instanceof Runtime);
Method exec2 = clazz.getDeclaredMethod("exec", String.class);
exec2.invoke(runtime2, "open /System/Applications/Calculator.app");
}
}
Filed命令执行
- 了解到
field.get
就可以获取变量值了
package Reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.Runtime;
public class ReflectRuntimeExec {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("java.lang.Runtime");
Field field = clazz.getDeclaredField("currentRuntime");
field.setAccessible(true);
Object runtime1 = field.get(clazz);
System.out.println(runtime1 instanceof Runtime);
Method exec1 = clazz.getDeclaredMethod("exec", String.class);
exec1.invoke(runtime1, "open /System/Applications/Calculator.app");
}
}
逆水行舟,不进则退。