# 源码分析 | 咋嘞?你的IDEA过期了吧!加个Jar包就破解了,为什么?

作者:小傅哥
博客:https://bugstack.cn (opens new window)

沉淀、分享、成长,让自己和他人都能有所收获!😄

# 一、前言介绍

2020年了,对于一个程序猿来说;

2020 = 1024 + 996 | 404 + 404 + 404 + 404 + 404
2021 = 1024 + 997
2022 = 1024 + 9106
2023 = 1024 + 9107
...
20xx = 从今年开始可怕
1
2
3
4
5
6

当你过了元旦,爽了周末,清早上班,拿起杯子,加点新(薪)水,打开电脑,收起烦恼,翘起小脚,上扬嘴角。一切就绪都准备好,好!撸代码!啊!!!IDEA duang duang duang,过期了!

微信公众号:bugstack虫洞栈 & IDEA 过期

脑瓜一热赶紧搜索破解码;

  • 第一个,失败
  • 第二个,失败
  • 第三个,失败
  • ...
  • 第N个,终于,破解了三个月,先用着,先用着,以后再说!

可能大部分伙伴都在搜各种一堆一大串的破解码往里面粘,一个个试到最后终于过了。但也有一部分老司机是不搜破解码的,他们使用jar包破解,有效期100年。

那么!本文并不引导用户都去使用破解版,像IDEA这么优秀,其实给你提供了很多选择;

  1. 如果你是学生可以免费使用
  2. 分为社区办和旗舰版,你可以使用社区版 Free, open-source
  3. 一般大公司都是有正版授权的,可以使用
  4. 如果你有开源项目也可以申请 IDEA 授权

所以,个人开发使用社区版本即可,不要使用破解。

好!回归正题,本文主要讲解是为什么放个Jar包就能破解,最后在使用一个jar进行破解演示。在以下章节中你可以学习到如下知识;

  • Java Agent 非硬编码式代理类,这也就是常说的探针技术
  • ASM 字节码编程简单使用
  • 工程打包额外加载其他 jar 方法
  • 最后是一个破解演示,仅适合个人学习使用

# 二、案例工程

我们通过一个案例工程来模拟破解过程是怎么做到的,其实每个版本的IDEA都在增强防护机制,破解也越来越难。

itstack-demo-code-idea
└── src
    ├── main
    │   ├── java
    │   │   └── org.itstack.demo
    │   │       └── JetbrainsCrack.java
    │   └── resources	
    │       └── META-INF	
    │           └── MANIFEST.MF
    └── test
         └── java
             ├── com.jetbrains.ls.newLicenses
             │   └── DecodeCertificates.java			 
             └── org.itstack.demo.test
                 └── ApiTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 三、环境配置

  1. JDK 1.8
  2. IDEA 2019.3.1
  3. asm-all 3.3.1

# 四、代码讲述

在案例中我们模拟 IDEA 有一个 DecodeCertificates 类,用于做授权码校验。之后通过我们的 java agent 编程模拟授权被破解。

# 1. Java Agent 介绍

在 JDK1.5 以后,JVM 提供了 agent 技术构建一个独立于应用程序的代理程序(即为Agent),用来协助监测、运行甚至替换其他JVM上的程序。使用它可以实现虚拟机级别的AOP功能。

# 2. ASM 介绍

ASM 是一个 JAVA 字节码分析、创建和修改的开源应用框架。在 ASM 中提供了诸多的API用于对类的内容进行字节码操作的方法。与传统的 BCEL 和 SERL 不同,在 ASM 中提供了更为优雅和灵活的操作字节码的方式。目前 ASM 已被广泛的开源应用架构所使用,例如:Spring、Hibernate 等。

# 3. 开始我们的模拟破解之路

JetbrainsCrack.java & Agent 操作类

/**
 * 博客:http://bugstack.cn
 * 公众号:bugstack虫洞栈 | 更多原处优质干货
 * Agent 类,所有程序启动只要配置了 -javaagent: 都会走到 premain 方法
 */
public class JetbrainsCrack {

    public static void premain(String args, Instrumentation inst) {
        System.out.println("**************************************");
        System.out.println("*       公众号:bugstack虫洞栈       *");
        System.out.println("*     博客:https://bugstack.cn      *");
        System.out.println("*   你用剑,我用刀,好的代码都很烧! *");
        System.out.println("**************************************");
        inst.addTransformer(new MethodEntryTransformer());
    }

    static class MethodEntryTransformer implements ClassFileTransformer {

        private Logger logger = LoggerFactory.getLogger(MethodEntryTransformer.class);

        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {

            try {
                if (className.equals("com/jetbrains/ls/newLicenses/DecodeCertificates")) {
                    ClassReader cr = new ClassReader(classfileBuffer);
                    ClassNode cn = new ClassNode();
                    cr.accept(cn, 0);
                    List<MethodNode> methodNodes = cn.methods;
                    for (MethodNode methodNode : methodNodes) {
                        if ("decodeLicense".equals(methodNode.name)) {
                            InsnList insns = methodNode.instructions;
                            //清除指令
                            insns.clear();
                            insns.add(new VarInsnNode(Opcodes.ALOAD, 1)); // 将本地指定的引用存入栈中
                            insns.add(new InsnNode(Opcodes.ARETURN));          // 从方法中返回引用类型的数据
                            // 访问结束
                            methodNode.visitEnd();
                            ClassWriter cw = new ClassWriter(0);
                            cn.accept(cw);
                            byte[] bytes = cw.toByteArray();
                            // 输出字节码到Class
                            this.outputClazz(bytes);
                            // 返回最新字节码
                            return cw.toByteArray();
                        }
                    }
                }
            } catch (Exception e) {
                return classfileBuffer;
            }

            return classfileBuffer;
        }

        private void outputClazz(byte[] bytes) {
            // 输出类字节码
            FileOutputStream out = null;
            try {
                out = new FileOutputStream("ASMDecodeCertificates.class");
                logger.info("ASM类输出路径:{}", (new File("")).getAbsolutePath());
                out.write(bytes);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (null != out) try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

}
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
  • 在这个类中可以看到有一个 premain 方法,这个是在被 java agent 处理后的程序入口,所有信息类和方法都会到这个入口
  • 之后我们使用 inst.addTransformer(new MethodEntryTransformer()); 添加我们自己的处理逻辑,这个逻辑也是用字节码编程技术代理类的过程。这个过程也就是我们平时开发中那些不需硬编码就可以监控方法执行时长的逻辑一样
  • MethodEntryTransformer 实现了 ClassFileTransformer 的 transform 方法,也就是真正操作字节码的过程。
    • 在这个类方法中首先需要找到我们的授权码校验类 com/jetbrains/ls/newLicenses/DecodeCertificates ,每一个版本的IDEA不一样,同时授权逻辑校验也不一样
    • 紧接着在找到授权校验类里面的校验方法,if ("decodeLicense".equals(methodNode.name))
    • 接下来就需要对字节码进行处理了,这里面的处理过程比较粗暴,直接将原来方法里的指令内容清空。然后使用 new VarInsnNode(Opcodes.ALOAD, 1) 将本地指定的引用存入栈中
    • 之后将我们的入参内容直接返回,new InsnNode(Opcodes.ARETURN),从方法中返回引用类型的数据。在以往旧版本的 IDEA 破解中比较简单,直接把最终需要的破解内容返回即可,里面描述了 IDEA 各个软件的使用期限
    • 最终将我们处理后的字节码返回给方法,return cw.toByteArray();这个时候虽然你大爷还是你大爷,但你大娘已经不是你大娘了
  • 为了测试的验证我们将变更后的字节码代码(大娘)输出到工程目录下,也就是一个 class 文件,下文测试时候验证

# 4. DecodeCertificates.java & 模拟 ideaIU-15.0.1 软件授权码校验类

public class DecodeCertificates {

    public String decodeLicense(String usingKey) {
        // 模拟校验授权码
        return "usingKey is error:"+ usingKey;
    }

}
1
2
3
4
5
6
7
8
  • 这个类比较简单只是模拟有这么个方法用于校验授权码

# 5. ApiTest.java & 测试类

/**
 * 博客:http://bugstack.cn
 * 公众号:bugstack虫洞栈 | 更多原处优质干货
 * 测试类配置 VM 参数
 * Idea VM options:-javaagent:E:\itstack\GIT\itstack.org\itstack-demo-code\itstack-demo-code-idea\target\itstack-demo-code-idea-1.0-SNAPSHOT.jar
 */
public class ApiTest {

    private static Logger logger = LoggerFactory.getLogger(ApiTest.class);

    public static void main(String[] args) throws Exception {
        DecodeCertificates decodeCertificates = new DecodeCertificates();
        // 模拟usingKey:认购有效期至2089年7月8日
        String license = decodeCertificates.decodeLicense("Subscription is active until July 8, 2089");
        logger.info("测试结果:{}", license);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 6. MANIFEST.MF 配置引导启动时加载

Manifest-Version: 1.0
Premain-Class: org.itstack.demo.JetbrainsCrack
Can-Redefine-Classes: true

1
2
3
4

# 7. POM 配置打包时加入ASM包

<!-- 将javassist包打包到Agent-->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <artifactSet>
            <includes>
                <include>asm:asm-all:jar:</include>
            </includes>
        </artifactSet>
    </configuration>
</plugin>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 五、工程测试

  1. 先单纯的直接运行ApiTest.java ,测试结果如下(模拟返回授权不可用);

    21:23:46.101 [main] INFO  org.itstack.demo.test.ApiTest - 测试结果:usingKey is error:Subscription is active until July 8, 2089
    
    1
  2. 第二步测试前先打包下工程,这个时候你会看到如下结果;

    [INFO] --- maven-install-plugin:2.4:install (default-install) @ itstack-demo-code-idea ---
    [INFO] Installing E:\itstack\GIT\itstack.org\itstack-demo-code\itstack-demo-code-idea\target\itstack-demo-code-idea-1.0-SNAPSHOT.jar to D:\Program Files (x86)\apache-maven-3.6.2\repository\org\itstack\demo\itstack-demo-code-idea\1.0-SNAPSHOT\itstack-demo-code-idea-1.0-SNAPSHOT.jar
    [INFO] Installing E:\itstack\GIT\itstack.org\itstack-demo-code\itstack-demo-code-idea\dependency-reduced-pom.xml to D:\Program Files (x86)\apache-maven-3.6.2\repository\org\itstack\demo\itstack-demo-code-idea\1.0-SNAPSHOT\itstack-demo-code-idea-1.0-SNAPSHOT.pom
    [INFO] Installing E:\itstack\GIT\itstack.org\itstack-demo-code\itstack-demo-code-idea\target\itstack-demo-code-idea-1.0-SNAPSHOT-sources.jar to D:\Program Files (x86)\apache-maven-3.6.2\repository\org\itstack\demo\itstack-demo-code-idea\1.0-SNAPSHOT\itstack-demo-code-idea-1.0-SNAPSHOT-sources.jar
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time:  3.080 s
    [INFO] Finished at: 2020-01-05T23:25:08+08:00
    [INFO] ------------------------------------------------------------------------
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  • 这里的 itstack-demo-code-idea-1.0-SNAPSHOT.jar 就是我们的 Agent 包,按实际情况复制自己的路径
  1. 配置 VM options:-javaagent:E:\itstack\GIT\itstack.org\itstack-demo-code\itstack-demo-code-idea\target\itstack-demo-code-idea-1.0-SNAPSHOT.jar

    微信公众号:bugstack虫洞栈 & IDEA VM options

  2. 运行 ApiTest 测试,正确结果如下;

    23:29:42.803 [main] INFO  org.itstack.demo.test.ApiTest - 测试结果:usingKey is error:Subscription is active until July 8, 2089
    
    Process finished with exit code 0
    
    1
    2
    3
  • 这个过程就是你使用 jar 包破解 IDEA 的过程,了解这个技术点可以用在很多不需要硬编码就能做到的服务中,比如监控,调试等
  1. 别忘了我们还在 Agent 中输出了新的字节码,看看这个时候的类是什么样(你大爷还是你大爷,但你大娘可不是你大娘了)

被代理前

public class DecodeCertificates {

	public String decodeLicense(String usingKey) {
		// 模拟校验授权码
		return "usingKey is error:"+ usingKey;
	}

}
1
2
3
4
5
6
7
8

被代理后

package com.jetbrains.ls.newLicenses;

public class DecodeCertificates {
	public DecodeCertificates() {
	}

	public String decodeLicense(String usingKey) {
		return usingKey;
	}
}
1
2
3
4
5
6
7
8
9
10

# 六、综上总结

  • 建议个人使用社区版即可,不要尝试破解尊重IDEA,本文只为学习 javaagent 技术
  • ASM 这个东西特别强大,其实字节码编程还有 javassist,在一起 RPC 框架中有非常多的使用
  • 关于 java agent 我已经专题方式写过案例文章,可以参考;https://bugstack.cn/itstack-demo-agent/itstack-demo-agent.html