WEB

WEB安全

漏洞复现

CTF

常用工具

实战

代码审计

Javaweb

后渗透

内网渗透

免杀

进程注入

权限提升

漏洞复现

靶机

vulnstack

vulnhub

Root-Me

编程语言

java

逆向

PE

逆向学习

HEVD

PWN

CTF

heap

其它

关于博客

面试

杂谈

Java反射

终于准备学习Java反序列化了,先放个反射弹计算器镇楼

1
2
3
4
5
6
7
8
9
10
11
import java.lang.reflect.Method;

public class reflect {
public static void main(String[] args) throws Exception {
Class exec = Class.forName("java.lang.Runtime");
Method ExecMethod = exec.getMethod("exec", String.class);
Method RuntimeMethod = exec.getMethod("getRuntime");
Object runtime = RuntimeMethod.invoke(exec);
ExecMethod.invoke(runtime, "calc");
}
}

本文的后续就围绕着这块代码来讲解

0x01 正射反射

上面的代码是一个反射的例子,那有反射肯定有正射,上面代码正射是什么样子呢

1
2
3
4
5
6
public class zs {
public static void main(String[] args) throws Exception {
Runtime runtime = Runtime.getRuntime();
runtime.exec("calc");
}
}

我敲这不就是类的实例化然后调用类中方法吗

那可以直接实例化为啥还要用反射呢

这里引入p神提到的名词动态特性

打个比方php中的一句话木马

1
<?php eval($_POST['pass']); ?>

一行php代码可以执行所有的php代码

在java中没有eval这种函数,如果我想实例化一个类就只能实例化一个类

不过有了反射之后情况就不同了,可以写一个方法将需要调用的类和类中方法作为参数传递到反射函数中去,可以在一定程度上实现任意执行java代码的功能

类似下面这样

1
2
3
4
public static void execute(String className, String methodName) throws Exception {
Class clazz = Class.forName(className);
clazz.getMethod(methodName).invoke(clazz.newInstance());
}

0x02 获取Class类方法介绍

Class.forName

要求JVM查找后加载指定类,初始化该类

注意这里是初始化类,不是实例化类,不会触发构造方法

但是会触发静态代码块中的代码

先来看下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//User.java
public class User {
public static int number = 222;
private static int pri_number = 222;
public int no_static_number = 666;
static {
System.out.println("静态");
}

public User(){
System.out.println("无参数构造");
}

public User(int a1){
System.out.println(a1 + "参数构造");
}
private User(String a1, String a2){
System.out.println(a1 + "私有构造函数" + a2);
}

public static int HelloWorld(){
System.out.println("HelloWorld");
return 0;
}

public static int HelloWorld(String id){
System.out.println("HelloWorld" + id);
return 0;
}

private static int Pri_HelloWorld(){
System.out.println("Pri_HelloWorld");
return 0;
}

private static int Pri_HelloWorld(String param){
System.out.println("Pri_HelloWorld" + param);
return 0;
}

public int NoStatic_HelloWorld(){
System.out.println("NoStatic_HelloWorld");
return 0;
}
public static void main(String[] args) {

System.out.println("");
}
}

反射代码

1
2
3
4
5
6
7
8
9
10
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class reflect {
public static void main(String[] args) throws Exception {
Class exec = Class.forName("User");
}
}

//输出:静态

这里就不贴图了,师傅们可以试试看,只会执行静态代码块中代码

getClass()

该方法用于获取实例化的对象的Class

1
2
3
4
5
6
7
8
9
10
11
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class reflect {
public static void main(String[] args) throws Exception {
User user = new User();
Class exec = user.getClass();
}
}
//静态
//无参数构造

实例化会触发构造方法

.class

该方法可以直接获取类的Class

1
2
3
4
5
6
7
8
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class reflect {
public static void main(String[] args) throws Exception {
Class exec = User.class;
}
}

不会触发静态代码块里的方法

没有任何输出

0x02 方法介绍

getMethod

获取成员方法的函数

1
getMethod(String name, Class<?>... parameterTypes)

第一个参数为成员方法名称,第二个参数为成员方法的类型

因为java中有重载所以会出现同名函数,可以通过指定后面的参数类型来确认调用什么函数

同样的还是User.java

这次的反射代码为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.lang.reflect.Method;

public class reflect {
public static void main(String[] args) throws Exception {
Class exec = Class.forName("User");
Method ExecMethod = exec.getMethod("HelloWorld");
ExecMethod.invoke(exec); //invoke后面再说
Method ExecMethod1 = exec.getMethod("HelloWorld", String.class);
ExecMethod1.invoke(exec, "00");
}
}
//静态
//HelloWorld
//HelloWorld00

可以看到调用根据getMethod的第二个参数可以调用不同参数的同名函数

要是成员方法为受保护的和私有的改怎么办

这时候就需要下面的方法了

getDeclaredMethod

1
2
3
4
5
6
7
8
9
10
11
12
import java.lang.reflect.Method;

public class reflect {
public static void main(String[] args) throws Exception {
Class exec = Class.forName("User");
Method ExecMethod = exec.getDeclaredMethod("Pri_HelloWorld");
ExecMethod.setAccessible(true);
ExecMethod.invoke(exec);
}
}

//Pri_HelloWorld

其实没有啥区别的

setAccessible可以启用和禁用安全检查,参数为true时则禁用

禁用后可以调用私有方法,别的就完全相同了

getField

该方法可以获取类中公开成员变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.lang.reflect.Field;

public class reflect {

public static void main(String[] args) throws Exception {
Class exec = Class.forName("User");
Field NumberField = exec.getField("number");
System.out.println(NumberField.get(exec));
NumberField.set(exec, 234);
System.out.println(NumberField.get(exec));
}
}
//静态
//222
//234

用get可以得到类中成员的值,set可以修改类中成员的值

同样的下面的方法可以得到类中受保护和私有

getDeclaredField

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.lang.reflect.Field;

public class reflect {

public static void main(String[] args) throws Exception {
Class exec = Class.forName("User");
Field NumberField = exec.getDeclaredField("pri_number");
NumberField.setAccessible(true);
System.out.println(NumberField.get(exec));
NumberField.set(exec, 234);
System.out.println(NumberField.get(exec));

}
}
//静态
//222
//234

关于这块最后再来看看下面的代码就可以结束了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class reflect {

public static void main(String[] args) throws Exception {
Class exec = Class.forName("User");
Field[] Field_ALL = exec.getDeclaredFields();
//取得全部成员变量
Field[] Field_Public = exec.getFields();
//取得所有公开的成员变量
Method[] Method_ALL = exec.getDeclaredMethods();
//得到类中所有的方法
Method[] Method_Public = exec.getMethods();
//得到类中所有公开的方法
}
}

invoke

这就是反射取得类后执行方法的函数

1
2
3
4
5
6
7
8
9
10
11
12
import java.lang.reflect.Method;

public class reflect {
public static void main(String[] args) throws Exception {
Class exec = Class.forName("User");
Method ExecMethod = exec.getDeclaredMethod("Pri_HelloWorld", String.class);
ExecMethod.setAccessible(true);
ExecMethod.invoke(exec, "test");
}
}
//静态
//Pri_HelloWorldtest

按照getDeclaredMethod的第二个参数来指定执行同名的什么函数

getMethod同理

invoke第一个参数为对象实例

后面的参数要与getDeclaredMethod指定的一致

这里需要注意下invoke的第一个参数,如果要取得的时静态方法和静态成员变量,第一个参数可以为null,不影响使用,可以看到上面在User.java中取得的方法和变量都是静态的

如果需要调用非静态的方法则需要把取得的类实例化

需要用到下面的函数

newInstance()

该方法可以实例化取得的类

可以触发构造函数

如果调用的方法和变量不为静态则需要实例化后再获取

下面看看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class reflect {
public static void main(String[] args) throws Exception {
Class exec = Class.forName("User");
Method ExecMethod = exec.getMethod("NoStatic_HelloWorld");
Field NumberField = exec.getField("no_static_number");
Object new_exec = exec.newInstance();
ExecMethod.invoke(new_exec);
System.out.println(NumberField.get(new_exec));
}
}

//Pri_HelloWorld
//静态
//无参数构造
//NoStatic_HelloWorld
//666

这样就可以调用非静态的方法和变量了

问题又来了,类中可能有多个构造方法,改怎么获取呢

需要用到下面的方法

getConstructor

该方法可以指定newInstance实例化使用什么构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.lang.reflect.Constructor;
public class reflect {
public static void main(String[] args) throws Exception {
Class exec = Class.forName("User");
Constructor gz = exec.getConstructor();
gz.newInstance();
Constructor gz1 = exec.getConstructor(int.class);
gz1.newInstance(1);
}
}
//静态
//无参数构造
//1参数构造

同样的下面的函数可以得到私有的构造方法

getDeclaredConstructor

1
2
3
4
5
6
7
8
9
10
11
import java.lang.reflect.Constructor;
public class reflect {
public static void main(String[] args) throws Exception {
Class exec = Class.forName("User");
Constructor pri_gz = exec.getDeclaredConstructor(String.class, String.class);
pri_gz.setAccessible(true);
pri_gz.newInstance("_","_");
}
}
//静态
//_私有构造函数_

同样的也有获取全部公开构造函数的方法和获取全部构造函数的方法

1
2
3
4
5
6
7
8
import java.lang.reflect.Constructor;
public class reflect {
public static void main(String[] args) throws Exception {
Class exec = Class.forName("User");
Constructor[] Constructor_ALL = exec.getDeclaredConstructors();
Constructor[] Constructor_Public = exec.getConstructors();
}
}