Javaのコンパイルと逆コンパイル

13401 ワード

Javaのコンパイルと逆コンパイル
コンパイルとは
コンパイルはC、C++、Javaなどの高級言語をアセンブリ言語、機械言語などの低級言語に変換する過程であり、低級言語はコンピュータの識別に有利であり、高級言語はプログラマーの編纂と読書に有利であり、この過程を実行するツールはコンパイラである.
Java言語にも独自のコンパイラjavacコマンドがあり、HelloWorld.javaのファイルを作成すると、javac HelloWorld.javaのファイルをコマンドHelloWorld.classでコンパイルすることができます.このclassファイルはJVMが認識できるファイルです.このプロセスはコンパイルのプロセスです.
実はclassファイルもマシンが認識できる言語ではなく、JVMはさらにclassバイトコードファイルをマシンが認識できるマシン言語に変換します.
逆コンパイルとは
コンパイルプロセスとは逆に、classファイルをjavaファイルに変換するプロセスであり、javaの逆コンパイルを通じて、東西java文法の背後にある原理を得ることがある.
Javaでよく使われる逆コンパイルツール
javap javapはjdkが持参したツールで、コードを逆コンパイルしたりjavaコンパイラが生成したバイトコードを表示したりすることができます.一例で理解できます.
以下はHelloWorld.javaのソースファイルです.switchで文字列をマッチングします.ソースコードは次のとおりです.
public class HelloWorld {

    public static void main(String[] args) {
        String str = "world";
        switch (str) {
            case "hello":
                System.out.println("hello");
                break;
            case "world":
                System.out.println("world");
                break;
            default:
                break;
        }
    }
}


このコードは簡単で、文字列を初期化することで常に明るくなり、switchで対応する文字列をマッチングして出力することができます.ソースコードから具体的な比較方法を分析するのは難しいです.このとき、コンパイル---逆コンパイルの方法を使うことができます.
まず、HelloWorld.classのバイトコードファイルをjavac HelloWorld.javaで生成することができます.このときidea、eclipseでプラグインを借りない場合、HelloWorld.classファイルの内容が読めません.
その後、classファイルをjavap -c HelloWorld.classで逆コンパイルし、次のように出力します.
Compiled from "HelloWorld.java"
public class HelloWorld {
  public HelloWorld();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String world
       2: astore_1
       3: aload_1
       4: astore_2
       5: iconst_m1
       6: istore_3
       7: aload_2
       8: invokevirtual #3                  // Method java/lang/String.hashCode:()I
      11: lookupswitch  { // 2
              99162322: 36
             113318802: 50
               default: 61
          }
      36: aload_2
      37: ldc           #4                  // String hello
      39: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      42: ifeq          61
      45: iconst_0
      46: istore_3
      47: goto          61
      50: aload_2
      51: ldc           #2                  // String world
      53: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      56: ifeq          61
      59: iconst_1
      60: istore_3
      61: iload_3
      62: lookupswitch  { // 2
                     0: 88
                     1: 99
               default: 110
          }
      88: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
      91: ldc           #4                  // String hello
      93: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      96: goto          110
      99: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
     102: ldc           #2                  // String world
     104: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     107: goto          110
     110: return
}


JAvap共通パラメータ:
-help   

-l         

-public    public    

-protected    public protected    

-package     ,public protected    ,     

-p -private         

-s         

-c         ,  ,        ,  java      ,

-verbose      ,       

-constants     final  


JAvapはバイトコードをjavaファイルに逆コンパイルするのではなく、バイトコードを理解できるように生成しました.実はjavapが生成したファイルは依然としてバイトコードで、プログラマーが少し理解できるだけです.バイトコードをマスターすれば、以上のコードが読めます.実はStringをhashcodeに変えて比較します.
一般的には、バイトコードを見る必要がある場合にのみjavapコマンドが必要であり、バイトコードから漏れた情報が最も完全であり、逆コンパイルで読みやすいclassファイルを生成したい場合には、以下の2つの神器を使用することができます.
jad
JADは比較的良い逆コンパイルツールで、実行ツールをダウンロードすればclassファイルの逆コンパイルを実現できます.それとも上のソースコードなのか、jadを使用して逆コンパイルすると次のようになります.
jad HelloWorld.class 
Parsing HelloWorld.class... Generating HelloWorld.jad

JAdの逆コンパイル後にHelloWorld.jadファイルが生成されます.内容は以下の通りです.
// 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:   HelloWorld.java

import java.io.PrintStream;

public class HelloWorld
{

    public HelloWorld()
    {
    }

    public static void main(String args[])
    {
        String s = "world";
        String s1 = s;
        byte byte0 = -1;
        switch(s1.hashCode())
        {
        case 99162322: 
            if(s1.equals("hello"))
                byte0 = 0;
            break;

        case 113318802: 
            if(s1.equals("world"))
                byte0 = 1;
            break;
        }
        switch(byte0)
        {
        case 0: // '\0'
            System.out.println("hello");
            break;

        case 1: // '\001'
            System.out.println("world");
            break;
        }
    }
}


その逆コンパイルされたコードはソースコードに近く、読みやすくなっています.switch操作は実際にはcaceのhashCodeからequalsの方法で2つの文字列を比較していることがわかります.
JAd共通パラメータ:
 -a -  JVM          
-af -   -a,            
-clear -         
-b -         (e.g., if(a) { b(); }, default: no) 
-d  -             
-dead -        dead   (default: no) 
-disass -              (no JAVA source generated) 
-f -        ,         
-ff -            (default: after methods) 
-i -                
-l -  strings             (default: no) 
-lnc -             (default: no) 
-nl -   strings      newline character (default: no) 
-nodos -     class     dos    (CR before NL, default: check) 
-nocast -          
-nocode -           
-noconv -     java     (default: do) 
-noctor -            
-noinner -          (default: turn on) 
-nolvt -            
-nonlb -                   (default: do) 
-o -            (default: no) 
-p -              STDOUT (e.g., for piping) 

  .    

jad -o -r -sjava -dsrc test.class

tree      *.class  
    jad -o -r -sjava -dsrc tree/**/*.class

    unix     :jad -o -r -sjava -dsrc 'tree/**/*.class'

           ,        

jad -p example1.class > myexm1.java

しかし、JADは長い間更新されていないため、Java 7で生成されたバイトコードを逆コンパイルする際に、サポートされていないという問題がたまに発生し、Java 8のlambda式を逆コンパイルする際には徹底的に失敗する
cfr
JADはとても使いやすくて、しかし仕方がないのはとても长い间更新していないので、1种の新しいツールで彼に代わることしかできなくて、CFRは1つの悪くない选択で、JADに比べて、彼の文法は少し复雑かもしれませんが、しかし彼は使うことができます.
CFRは、現代Javaの特性であるJava 8 lambdas(Javaおよびそれ以前のバージョンのJava beta 103)を逆コンパイルし、Java 7 Stringを逆コンパイルするが、CFRは完全にJava 6で記述する.
cfrのjarパッケージはhttp://www.benf.org/other/cfr/cfr_0_129.jarダウンロード
逆コンパイル処理の実行
java -jar /Users/home/Desktop/cfr_0_129.jar  HelloWorld.class --decodestringswitch false

出力は次のとおりです.
src $ java -jar /Users/tongkun/Desktop/cfr_0_129.jar  HelloWorld.class --decodestringswitch false
/*
 * Decompiled with CFR 0_129.
 */
import java.io.PrintStream;

public class HelloWorld {
    public static void main(String[] arrstring) {
        String string;
        String string2 = string = "world";
        int n = -1;
        switch (string2.hashCode()) {
            case 99162322: {
                if (!string2.equals("hello")) break;
                n = 0;
                break;
            }
            case 113318802: {
                if (!string2.equals("world")) break;
                n = 1;
            }
        }
        switch (n) {
            case 0: {
                System.out.println("hello");
                break;
            }
            case 1: {
                System.out.println("world");
                break;
            }
        }
    }
}


jad実行の結果と同様にパラメータが
  • --decodestringswitchは、switchがstringをサポートする詳細を復号することを示す.
  • に類似しているのは、--decodeenumswitch--decodefinally--decodelambdasなどである.
  • --decodelambdasは、lambda式を逆コンパイルすることができる.
  • java -jar cfr_0_125.jar --helpでは、どのcfrパラメータがあるかを知ることができますが、ここでは説明しません.
    CFR 0_129
    
       --aexagg                         (boolean) 
       --aggressivesizethreshold        (int >= 0)  default: 15000
       --allowcorrecting                (boolean)  default: true
       --analyseas                      (One of [JAR, WAR, CLASS]) 
       --arrayiter                      (boolean)  default: true if class file from version 49.0 (Java 5) or greater
       --caseinsensitivefs              (boolean)  default: false
       --clobber                        (boolean) 
       --collectioniter                 (boolean)  default: true if class file from version 49.0 (Java 5) or greater
       --commentmonitors                (boolean)  default: false
       --comments                       (boolean)  default: true
       --decodeenumswitch               (boolean)  default: true if class file from version 49.0 (Java 5) or greater
       --decodefinally                  (boolean)  default: true
       --decodelambdas                  (boolean)  default: true if class file from version 52.0 (Java 8) or greater
       --decodestringswitch             (boolean)  default: true if class file from version 51.0 (Java 7) or greater
       --dumpclasspath                  (boolean)  default: false
       --eclipse                        (boolean)  default: true
       --elidescala                     (boolean)  default: false
       --extraclasspath                 (string) 
       --forcecondpropagate             (boolean) 
       --forceexceptionprune            (boolean) 
       --forcereturningifs              (boolean) 
       --forcetopsort                   (boolean) 
       --forcetopsortaggress            (boolean) 
       --forloopaggcapture              (boolean) 
       --hidebridgemethods              (boolean)  default: true
       --hidelangimports                (boolean)  default: true
       --hidelongstrings                (boolean)  default: false
       --hideutf                        (boolean)  default: true
       --ignoreexceptions               (boolean)  default: false
       --innerclasses                   (boolean)  default: true
       --j14classobj                    (boolean)  default: false if class file from version 49.0 (Java 5) or greater
       --jarfilter                      (string) 
       --labelledblocks                 (boolean)  default: true
       --lenient                        (boolean)  default: false
       --liftconstructorinit            (boolean)  default: true
       --outputdir                      (string) 
       --outputpath                     (string) 
       --override                       (boolean)  default: true if class file from version 50.0 (Java 6) or greater
       --pullcodecase                   (boolean)  default: false
       --recover                        (boolean)  default: true
       --recovertypeclash               (boolean) 
       --recovertypehints               (boolean) 
       --relinkconststring              (boolean)  default: true
       --removebadgenerics              (boolean)  default: true
       --removeboilerplate              (boolean)  default: true
       --removedeadmethods              (boolean)  default: true
       --removeinnerclasssynthetics     (boolean)  default: true
       --rename                         (boolean)  default: false
       --renamedupmembers              
       --renameenumidents              
       --renameillegalidents           
       --renamesmallmembers             (int >= 0)  default: 0
       --showinferrable                 (boolean)  default: false if class file from version 51.0 (Java 7) or greater
       --showops                        (int >= 0)  default: 0
       --showversion                    (boolean)  default: true
       --silent                         (boolean)  default: false
       --stringbuffer                   (boolean)  default: false if class file from version 49.0 (Java 5) or greater
       --stringbuilder                  (boolean)  default: true if class file from version 49.0 (Java 5) or greater
       --sugarasserts                   (boolean)  default: true
       --sugarboxing                    (boolean)  default: true
       --sugarenums                     (boolean)  default: true if class file from version 49.0 (Java 5) or greater
       --tidymonitors                   (boolean)  default: true
       --tryresources                   (boolean)  default: true if class file from version 51.0 (Java 7) or greater
       --usenametable                   (boolean)  default: true
       --help                           (string) 
    
    
    

    JD-GUI
    JD-GUIはC++で開発されたJava逆コンパイルツールで、Pavel Kouznetsovが開発し、Windows、Linux、アップルMac Osの3つのプラットフォームをサポートしています.また、Eclipseプラットフォームの下にあるプラグインJD-Eclipseも提供されています.JD-GUIはGPLv 3オープンソースプロトコルに基づいて、個人用には完全に無料です.JD-GUIは主に可視化操作を提供し、直接ファイルをウィンドウにドラッグすることができ、効果図は以下の通りである.
    興味があればhttp://jd.benow.ca/GUIおよびidea、eclipseの逆コンパイルプラグインをダウンロードします.
    JD-GUIの使用も非常に便利で、jarパッケージやclassファイルをJD-GUIインタフェースにドラッグしたり開いたりすれば逆コンパイルが完了するので、ここでは説明を思い出しません.
    参考感謝:
    https://blog.csdn.net/u011479200/article/details/80019827
    https://blog.csdn.net/dongnan591172113/article/details/51832628
    http://jd.benow.ca/