Day11 DCL懒汉式单例反破解攻坚战 (๑•̀ㅂ•́)و✧
uwupu 啦啦啦啦啦

DCL懒汉式单例反破解攻坚战 (๑•̀ㅂ•́)و✧

DCL懒汉式单例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//懒汉式单例
public class LazyMan {
private volatile static LazyMan lazyMan;//添加volatile,防止代码重新编排。具体影响位置:getInstance()下的lazyMan = new LazyMan();

//双重监测锁模式的 懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if(lazyMan==null){
synchronized (LazyMan.class){ //双重检测锁模式 避免对象反复创建
if(lazyMan==null){
lazyMan = new LazyMan();
}

}
}
return lazyMan;
}
private LazyMan(){

}
}

通过反射破解DCL懒汉式单例

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Crack1 {
//通过反射获取多个LazyManV2
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
LazyManV2 lazyManV2_1 = LazyManV2.getInstance();//第一个
Class c1 = LazyManV2.class;//获取LazyManV2的Class类
Constructor constructor = c1.getDeclaredConstructor();//通过反射获取构造方法
constructor.setAccessible(true);//设置权限
LazyManV2 lazyManV2_2 = (LazyManV2) constructor.newInstance();//创建对象
System.out.println(lazyManV2_1);
System.out.println(lazyManV2_2);

}
}
1
2
3
4
5
6
main线程创建了一个DCL LazyMan
main线程创建了一个DCL LazyMan
com.yn.Demo31_懒汉式单例破解攻坚战.LazyManV2@1b6d3586
com.yn.Demo31_懒汉式单例破解攻坚战.LazyManV2@4554617c

Process finished with exit code 0

成功创建两个不同的单例。

DCL懒汉式单例2.0

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
//懒汉式单例
public class LazyManV3 {
private static LazyManV3 lazyMan;

//双重监测锁模式的 懒汉式单例 DCL懒汉式
public static LazyManV3 getInstance(){
if(lazyMan==null){
synchronized (LazyManV3.class){
if(lazyMan==null){
lazyMan = new LazyManV3();
}

}
}
return lazyMan;
}
private LazyManV3(){
//防止对象反复创建 防止通过反射破坏单例
synchronized (LazyManV3.class){
if(lazyMan!=null){
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
System.out.println(Thread.currentThread().getName()+ "线程创建了一个DCL LazyMan");
}
}

这里在构造方法中添加了语句,在getInstance()方法创建对象后,禁止通过反射方式再次创建对象。

使用getInstance()创建对象后,再次使用反射方式创建对象会抛出异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
main线程创建了一个DCL LazyMan
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.yn.Demo31_懒汉式单例破解攻坚战.Crack2.main(Crack2.java:13)
Caused by: java.lang.RuntimeException: 不要试图使用反射破坏异常
at com.yn.Demo31_懒汉式单例破解攻坚战.LazyManV3.<init>(LazyManV3.java:24)
... 5 more

Process finished with exit code 1

通过反射破解DCL懒汉式单例2.0

道高一尺,魔高一丈。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Crack2 {
//通过反射获取多个LazyManV2
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// LazyManV3 lazyMan_1 = LazyManV3.getInstance();//第一个
Class c1 = LazyManV3.class;
Constructor constructor = c1.getDeclaredConstructor();
constructor.setAccessible(true);
LazyManV3 lazyMan_2 = (LazyManV3) constructor.newInstance();
LazyManV3 lazyMan_1 = (LazyManV3) constructor.newInstance();
System.out.println(lazyMan_1);
System.out.println(lazyMan_2);

}
}

两个对象都通过反射方式创建,这样LazyMan的类变量不会得到赋值,可以成功创建两个不同的对象。

DCL懒汉式单例3.0

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
//懒汉式单例
public class LazyManV4 {
private static LazyManV4 lazyMan;
private static boolean awsl = false;//红绿灯 awsl换成加密的字符串,这里学习用

//双重监测锁模式的 懒汉式单例 DCL懒汉式
public static LazyManV4 getInstance(){
if(lazyMan==null){
synchronized (LazyManV4.class){
if(lazyMan==null){
lazyMan = new LazyManV4();
}

}
}
return lazyMan;
}
private LazyManV4(){
//防止对象反复创建 防止通过反射破坏单例
synchronized (LazyManV4.class){
if(!awsl){//创建红绿灯,用于判断对象是否被反复创建
awsl=true;
}else{
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
System.out.println(Thread.currentThread().getName()+ "线程创建了一个DCL LazyMan");
}
}

添加红绿灯,用于判断对象是否被反复创建。

构造方法被重复调用,除第一次外,后面都会抛出异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
main线程创建了一个DCL LazyMan
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.yn.Demo31_懒汉式单例破解攻坚战.Crack3.main(Crack3.java:14)
Caused by: java.lang.RuntimeException: 不要试图使用反射破坏异常
at com.yn.Demo31_懒汉式单例破解攻坚战.LazyManV4.<init>(LazyManV4.java:27)
... 5 more

Process finished with exit code 1

通过反射破解DCL懒汉式单例3.0

解密并想办法获得红绿灯的变量,然后修改红绿灯的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Crack3 {
//通过反射获取多个LazyManV2
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
// LazyManV3 lazyMan_1 = LazyManV3.getInstance();//第一个
Class c1 = LazyManV4.class;
Constructor constructor = c1.getDeclaredConstructor();
constructor.setAccessible(true);
LazyManV4 lazyMan_2 = (LazyManV4) constructor.newInstance();

//先想办法解密获得awsl变量,然后通过反射修改awsl的值,然后破坏单例
// String awsl = c1.getDeclaredFields()[1].getName();
Field awsl = c1.getDeclaredField("awsl");
awsl.setAccessible(true);//破坏权限
awsl.set(LazyManV4.class,false);//设置值


LazyManV4 lazyMan_1 = (LazyManV4) constructor.newInstance();
System.out.println(lazyMan_1);
System.out.println(lazyMan_2);

}
}

枚举是一个单例对象

newInstance()的源代码:不能通过反射破坏枚举类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}

通过newInstance()的源代码发现,newInstance()不能通过反射破坏枚举类型。

枚举自带单例模式。

枚举

1
2
3
4
5
6
public enum Enum1 {
INSTANCE;
public static Enum1 getInstance(){
return INSTANCE;
}
}

通过反射破解枚举的单例对象

Idea反编译Enmu1的class,发现代码里面有一个无参构造方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public enum Enum1 {
INSTANCE;

private int asd;

private Enum1() {
}

public static Enum1 getInstance() {
return INSTANCE;
}

public String toString() {
return "Enum1{asd=" + this.asd + '}';
}
}

用反射的方式,通过无参构造方法创建对象。

1
2
3
4
5
6
7
8
9
10
11
12
public class Crack4 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
Enum1 enum1 = Enum1.getInstance();

Constructor<Enum1> constructor = Enum1.class.getDeclaredConstructor();//获取无参构造方法
constructor.setAccessible(true);//设置权限
Enum1 enum2 = constructor.newInstance();//创建对象
System.out.println(enum1);
System.out.println(enum2);

}
}
1
2
3
4
5
6
Exception in thread "main" java.lang.NoSuchMethodException: com.yn.Demo31_懒汉式单例破解攻坚战.Enum1.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.getDeclaredConstructor(Class.java:2178)
at com.yn.Demo31_懒汉式单例破解攻坚战.Crack4.main(Crack4.java:11)

Process finished with exit code 1

出现错误:没有这个方法。

结论:没有相关的无参构造方法。

通过javap -p 反编译class字节码文件,查看源代码。

也许Idea骗了我们?

1
2
3
4
5
6
7
8
9
10
11
12
Compiled from "Enum1.java"
public final class com.yn.Demo31_懒汉式单例破解攻坚战.Enum1 extends java.lang.Enum<com.yn.Demo31_懒汉式单例破解攻坚战.Enum1> {
public static final com.yn.Demo31_懒汉式单例破解攻坚战.Enum1 INSTANCE;
private int asd;
private static final com.yn.Demo31_懒汉式单例破解攻坚战.Enum1[] $VALUES;
public static com.yn.Demo31_懒汉式单例破解攻坚战.Enum1[] values();
public static com.yn.Demo31_懒汉式单例破解攻坚战.Enum1 valueOf(java.lang.String);
private com.yn.Demo31_懒汉式单例破解攻坚战.Enum1();
public static com.yn.Demo31_懒汉式单例破解攻坚战.Enum1 getInstance();
public java.lang.String toString();
static {};
}

通过反编译得知

  1. public final class com.yn.Demo31_懒汉式单例破解攻坚战.Enum1 extends java.lang.Enum<com.yn.Demo31_懒汉式单例破解攻坚战.Enum1>。枚举确实是一个用class修饰的类,不过是继承了java.lang.Enum成为枚举。
  2. private com.yn.Demo31_懒汉式单例破解攻坚战.Enum1();。Enmu1类确实有一个无参构造方法。?!

尝试使用jad反编译class字节码文件

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
package com.yn.Demo31_61D26C495F0F53554F8B783489E3653B575A6218;


public final class Enum1 extends Enum
{

public static Enum1[] values()
{
return (Enum1[])$VALUES.clone();
}

public static Enum1 valueOf(String name)
{
return (Enum1)Enum.valueOf(com/yn/Demo31_61D26C495F0F53554F8B783489E3653B575A6218/Enum1, name);
}

private Enum1(String s, int i)
{
super(s, i);
}

public static Enum1 getInstance()
{
return INSTANCE;
}

public String toString()
{
return (new StringBuilder()).append("Enum1{asd=").append(asd).append('}').toString();
}

public static final Enum1 INSTANCE;
private int asd;
private static final Enum1 $VALUES[];

static
{
INSTANCE = new Enum1("INSTANCE", 0);
$VALUES = (new Enum1[] {
INSTANCE
});
}
}

由反编译结果可知,private Enum1(String s, int i)是一个有参构造方法。

通过有参构造方法创建对象

1
2
3
4
5
6
7
8
9
10
11
12
public class Crack4 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
Enum1 enum1 = Enum1.getInstance();

Constructor<Enum1> constructor = Enum1.class.getDeclaredConstructor(String.class,int.class);//获取有参构造方法
constructor.setAccessible(true);//设置权限
Enum1 enum2 = constructor.newInstance();//创建对象
System.out.println(enum1);
System.out.println(enum2);

}
}
1
2
3
4
5
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
at com.yn.Demo31_懒汉式单例破解攻坚战.Crack4.main(Crack4.java:13)

Process finished with exit code 1

通过输出得知,不能通过反射创建枚举对象。

其他

enum枚举

枚举本身是一个class类

使用jad将class文件转换为java文件

jad -sjava 字节码文件.class

 评论