枚举单例抗反射攻击演示(一)
-
NoSuchMethodException
报的是枚举类EnumInstance
中没有
getDeclaredConstructor()
这个方法; - 意思是
EnumInstance
枚举类中没有无参构造器;
public enum EnumInstance {
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumInstance getInstance() {
return INSTANCE;
}
}
import java.lang.reflect.Constructor;
public class Test {
public static void main(String[] args) throws Exception {
reflectionAttack();
}
public static void reflectionAttack() throws Exception {
Class objectClass = EnumInstance.class;
Constructor constructor = objectClass.getDeclaredConstructor();
constructor.setAccessible(true);
}
}
输出:
Exception in thread "main" java.lang.NoSuchMethodException: designpattern.creational.singleton.enuminstance.EnumInstance.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.getDeclaredConstructor(Class.java:2178)
at designpattern.creational.singleton.enuminstance.Test.reflectionAttack(Test.java:14)
at designpattern.creational.singleton.enuminstance.Test.main(Test.java:9)
攻击失败源码分析(一)
-
Enum
类中只有一个构造器,该构造器有 2 个参数,所以在用objectClass.getDeclaredConstructor()
获取Enum
的无参构造器时是无法获得的;
枚举单例抗反射攻击演示(二)
- 获取到了
Enum
类的构造器还是无法用反射创建出EnumInstance
类的实例; - 这次报的错不一样,是
IllegalArgumentException: Cannot reflectively create enum objects
,说得很明显,不允许以反射的方式创建enum
实例;
public class Test {
public static void main(String[] args) throws Exception {
reflectionAttack2();
}
public static void reflectionAttack2() throws Exception {
Class objectClass = EnumInstance.class;
Constructor constructor = objectClass.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
EnumInstance instance = (EnumInstance)constructor.newInstance("", 0);
}
}
输出:
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
at designpattern.creational.singleton.enuminstance.Test.reflectionAttack2(Test.java:16)
at designpattern.creational.singleton.enuminstance.Test.main(Test.java:9)
攻击失败源码分析(二)
-
java.lang.reflect.Constructor
类在newInstance(Object ... initargs)
方法中针对需要被反射创建的对象的类型,专门针对Enum
做了判断,在 Java 层面禁止了反射创建枚举实例的可能;
EnumInstance 反编译分析其抗反射攻击原因
- 反编译工具 jad 下载地址:https://varaneckas.com/jad/
- 反编译
EnumInstance
;
>.\jad .\EnumInstance.class
Parsing .\EnumInstance.class... Generating EnumInstance.jad
反编译结果
- 很优雅的单例类的写法;
- 私有构造器
EnumInstance(String s, int i);
-
getInstance()
方法; public static final EnumInstance INSTANCE;
- 在静态代码块中初始化,饿汉模式(可用防御代码抵御反射攻击);
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: EnumInstance.java
package designpattern.creational.singleton.enuminstance;
public final class EnumInstance extends Enum
{
public static EnumInstance[] values()
{
return (EnumInstance[])$VALUES.clone();
}
public static EnumInstance valueOf(String name)
{
return (EnumInstance)Enum.valueOf(designpattern/creational/singleton/enuminstance/EnumInstance, name);
}
private EnumInstance(String s, int i)
{
super(s, i);
}
public Object getData()
{
return data;
}
public void setData(Object data)
{
this.data = data;
}
public static EnumInstance getInstance()
{
return INSTANCE;
}
public static final EnumInstance INSTANCE;
private Object data;
private static final EnumInstance $VALUES[];
static
{
INSTANCE = new EnumInstance("INSTANCE", 0);
$VALUES = (new EnumInstance[] {
INSTANCE
});
}
}
在枚举单例中定义方法
- 定义完的方法要在
EnumInstance
中定义抽象方法;
public enum EnumInstance {
INSTANCE {
protected void printTest() {
System.out.println("EmumInstance.INSTANCE.printTest()...");
}
};
protected abstract void printTest();
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumInstance getInstance() {
return INSTANCE;
}
}
public class Test {
public static void main(String[] args) throws Exception {
EnumInstance instance = EnumInstance.getInstance();
instance.printTest();
}
}
输出:
EmumInstance.INSTANCE.printTest()...
反编译带方法的EnumInstance
- 变成抽象类了,所以要把定义好的方法声明成抽象方法;
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: EnumInstance.java
package designpattern.creational.singleton.enuminstance.addmethod;
import java.io.PrintStream;
public abstract class EnumInstance extends Enum
{
public static EnumInstance[] values()
{
return (EnumInstance[])$VALUES.clone();
}
public static EnumInstance valueOf(String name)
{
return (EnumInstance)Enum.valueOf(designpattern/creational/singleton/enuminstance/addmethod/EnumInstance, name);
}
private EnumInstance(String s, int i)
{
super(s, i);
}
protected abstract void printTest();
public Object getData()
{
return data;
}
public void setData(Object data)
{
this.data = data;
}
public static EnumInstance getInstance()
{
return INSTANCE;
}
public static final EnumInstance INSTANCE;
private Object data;
private static final EnumInstance $VALUES[];
static
{
INSTANCE = new EnumInstance("INSTANCE", 0) {
protected void printTest()
{
System.out.println("EmumInstance.INSTANCE.printTest()...");
}
}
;
$VALUES = (new EnumInstance[] {
INSTANCE
});
}
}