源碼分析 | 咋嘞?你的IDEA過時了吧!加個Jar包就破解了,爲何?

微信公衆號:bugstack蟲洞棧 | 博客:bugstack.cn
沉澱、分享、成長,專一於原創專題案例,以最易學習編程的方式分享知識,讓本身和他人都能有所收穫。目前已完成的專題有;Netty4.x實戰專題案例、用Java實現JVM、基於JavaAgent的全鏈路監控、手寫RPC框架、架構設計專題案例、源碼分析等。
你用劍🗡、我用刀🔪,好的代碼都很燒😏,望你不吝出招💨!html

1、前言介紹

2020年了,對於一個程序猿來講;java

2020 = 1024 + 996 | 404 + 404 + 404 + 404 + 404
2021 = 1024 + 997
2022 = 1024 + 9106
2023 = 1024 + 9107
...
20xx = 從今年開始可怕
複製代碼

當你過了元旦,爽了週末,清早上班,拿起杯子,加點新(薪)水,打開電腦,收起煩惱,翹起小腳,上揚嘴角。一切就緒都準備好,好!擼代碼!啊!!!IDEA duang duang duang,過時了!apache

微信公衆號:bugstack蟲洞棧 & IDEA 過時

腦瓜一熱趕忙搜索破解碼;編程

  • 第一個,失敗
  • 第二個,失敗
  • 第三個,失敗
  • ...
  • 第N個,終於,破解了三個月,先用着,先用着,之後再說!

可能大部分夥伴都在搜各類一堆一大串的破解碼往裏面粘,一個個試到最後終於過了。但也有一部分老司機是不搜破解碼的,他們使用jar包破解,有效期100年。微信

那麼!本文並不想引導用戶都去使用破解版,像IDEA這麼優秀,其實給你提供了不少選擇;架構

  1. 若是你是學生能夠無償使用
  2. 分爲社區版和旗艦版,你可使用社區版 Free, open-source
  3. 通常大公司都是有正版受權的,可使用
  4. 若是你有開源項目也能夠申請 IDEA 受權

因此,我的開發使用社區版本便可,不要使用破解。框架

好!迴歸正題,本文主要講解是爲何放個Jar包就能破解,最後在使用一個jar進行破解演示。在如下章節中你能夠學習到以下知識;maven

  • Java Agent 非硬編碼式代理類,這也就是常說的探針技術
  • ASM 字節碼編程簡單使用
  • 工程打包額外加載其餘 jar 方法
  • 最後是一個破解演示,僅適合我的學習使用

2、案例工程

咱們經過一個案例工程來模擬破解過程是怎麼作到的,其實每一個版本的IDEA都在加強防禦機制,破解也愈來愈難。ide

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
複製代碼

3、環境配置

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

4、代碼講述

在案例中咱們模擬 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();
                }
            }
        }

    }

}
複製代碼
  • 在這個類中能夠看到有一個 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;
    }

}
複製代碼
  • 這個類比較簡單只是模擬有這麼個方法用於校驗受權碼

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);
    }

}
複製代碼

6. MANIFEST.MF 配置引導啓動時加載

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

複製代碼

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>
複製代碼

5、工程測試

  1. 先單純的直接運行ApiTest.java ,測試結果以下(模擬返回受權不可用);

    21:23:46.101 [main] INFO  org.itstack.demo.test.ApiTest - 測試結果:usingKey is error:Subscription is active until July 8, 2089
    複製代碼
  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] ------------------------------------------------------------------------ 複製代碼
  • 這裏的 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
    複製代碼
    • 這個過程就是你使用 jar 包破解 IDEA 的過程,瞭解這個技術點能夠用在不少不須要硬編碼就能作到的服務中,好比監控,調試等
  3. 別忘了咱們還在 Agent 中輸出了新的字節碼,看看這個時候的類是什麼樣(你大爺仍是你大爺,但你大娘可不是你大娘了)

被代理前

public class DecodeCertificates {

	public String decodeLicense(String usingKey) {
		// 模擬校驗受權碼
		return "usingKey is error:"+ usingKey;
	}

}
複製代碼

被代理後

package com.jetbrains.ls.newLicenses;

public class DecodeCertificates {
	public DecodeCertificates() {
	}

	public String decodeLicense(String usingKey) {
		return usingKey;
	}
}
複製代碼

6、綜上總結

  • 建議我的使用社區版便可,不要嘗試破解尊重IDEA,本文只爲學習 javaagent 技術
  • ASM 這個東西特別強大,其實字節碼編程還有 javassist,在一塊兒 RPC 框架中有很是多的使用
  • 關於 java agent 我已經專題方式寫過案例文章,能夠參考;bugstack.cn/itstack-dem…

7、關注公衆號

微信公衆號:bugstack蟲洞棧
相關文章
相關標籤/搜索