JAD - the fast JAva Decompiler

Zpracovala: Marta Václavíková
Vedoucí projektu, korektury, závěrečná revize: Pavel Herout

Jad je Java dekompilátor, tj. program, který čte jeden nebo více Java class souborů a konvertuje je zpět do zdrojových souborů java, jež mohou být většinou (viz též dále) opět zkompilovány.

Jad je napsán celý v C++ a většinou pracuje rychleji než dekompilátory napsané v Javě.

Jad je zdarma pouze pro nekomerční použití.

Obsah


Typická použití


Instalace

Jad nepoužívá JVM pro svůj běh, není nutná žádná instalace.

Stačí rozbalit soubor jad.zip, který obsahuje 2 soubory:

Spuštění

Pro dekompilaci jednoho class souboru Priklad1.class stačí napsat:

jad Priklad1.class
Vznikne soubor Priklad1.jad v aktuálním adresáři.
Jestliže daný soubor již existuje, program se zeptá, zda jej má přepsat.

Zadáme-li název class souboru třídy obsahující vnitřní třídy, program sám vyhledá v aktuálním adresáři class soubory daných vniřních tříd. Nenalezne-li je, vypíše upozornění.

Anonymní vnitřní třídy umístí do konstruktoru dané vnější třídy (což pak většinou znamená, že zdrojový soubor nelze okamžitě přeložit programem javac.exe, případně jej lze přeložit, ale výsledný program není funkční).

Dekompilace všech class souborů v aktuálním adresáři:
jad *.class
Dekompilace všech class souborů v podadresářích (poměrně netypický zápis pomocí ** znamená "všechny případné podadresáře"):
jad adr1/**/*.class

Nejdůležitější parametry

-o

Přepíše již existující soubor s příponou.jad bez varování a následného schválení.

-sjava

Výstupní soubor má příponu .java.

Např.: jad -sjava priklad1.class vytvoří soubor priklad1.java

-p [soubor class] > [jméno výstupního souboru]

Výsledek činnosti jad je posílán na standardní výstup. Typické použití je v případě, kdy chceme výstupní soubor pojmenovat zcela libovolným jménem.

Např. jad -p priklad1.class > pokus.java vytvoří soubor pokus.java

-d [název adresáře]

Umístí výstupní soubory do uvedeného adresáře.

-a

Přidá do výstupního souboru zdrojový kód v JVM bytecode. Funguje tedy podobně jako javap.exe.

Příklad na použití parametrů

Příkaz jad -o -r -sjava -d src tree/**/*.class
dekompiluje všechny soubory v podadresářích adresáře tree a umístí výstupní soubory do adresáře src podle názvů balíků. Vzniklé zdrojové soubory budou mít příponu .java (parametr -sjava) a budou-li existovat jejich starší verze, budou bez varování přepsány (parametr -o).
Například ze souboru tree/a/b/C.class vytvoří výstupní soubor src/a/b/C.java.

Příklady použití

Vnější a vnitřní třídy

Ukázkový Hlavni.java soubor obsahuje dvě vnější třídy Hlavni, Vnejsi a rozhraní Rozhrani. Třída Hlavni obsahuje vnitřní třídu Vnitrni a anonymní vnitřní třídu.

Pomocí příkazu javac Hlavni.java se vytvoří pět class souborů: Hlavni.class, Rozhrani.class, Vnejsi.class, Hlavni$1.class, Hlavni$Vnitrni.class

Zadáním příkazu jad Hlavni.class Vnejsi.class Rozhrani.class vzniknou výstupní soubory Hlavni.jad, Rozhrani.jad, Vnejsi.jad.

Soubor Hlavni.java
public class Hlavni {
   public int hlavniAtribut;
Soubor Hlavni.jad
import java.io.PrintStream;

public class Hlavni {
   public int hlavniAtribut;
   class Vnitrni {
      private int vnitrniAtribut;	

      public Vnitrni(int i) { 
         vnitrniAtribut = i; 
      }

      public void vnitrniMetoda() {
         System.out.println("Vnitřní metoda - " + vnitrniAtribut);
      }
   }
   class Vnitrni {
      public void vnitrniMetoda() {
         System.out.println("Vnit\u0159n\355 metoda - " + vnitrniAtribut);
      }

      private int vnitrniAtribut;

      public Vnitrni(int i) {
          vnitrniAtribut = i;
      }
   }
   public Rozhrani r = new Rozhrani () {
      public void metodaRozhrani () {
         System.out.println("Vnitřní Anonymní Metoda");
      }
   };
   public Rozhrani r;
   public Hlavni() {
      r = new Rozhrani() {
         public void metodaRozhrani() {
            System.out.println("Vnit\u0159n\355 Anonymn\355 Metoda");
         }
      };
   }
   public void hlavniMetoda() {
      System.out.println("Hlavní Metoda - " + hlavniAtribut);
      Vnitrni vn = new Vnitrni(1);
      vn.vnitrniMetoda();
   }
   public void hlavniMetoda() {
      System.out.println("Hlavn\355 Metoda - " + hlavniAtribut);
      Vnitrni vnitrni = new Vnitrni(1);
      vnitrni.vnitrniMetoda();
   }
   public static void main(String[] args){
      Hlavni h = new Hlavni();
      Vnejsi v = new Vnejsi(2);
      h.hlavniAtribut = 5;
      h.hlavniMetoda();
      v.vnejsiMetoda();
      h.r.metodaRozhrani();
   }
   public static void main(String args[]) {
      Hlavni hlavni = new Hlavni();
      Vnejsi vnejsi = new Vnejsi(2);
      hlavni.hlavniAtribut = 5;
      hlavni.hlavniMetoda();
      vnejsi.vnejsiMetoda();
      hlavni.r.metodaRozhrani();
    }
interface Rozhrani {
  void metodaRozhrani();
}
Soubor Rozhrani.jad
interface Rozhrani {
    public abstract void metodaRozhrani();
}
class Vnejsi {
   public int vnejsiAtribut;

   public Vnejsi (int i) { 
      vnejsiAtribut = i; 
   }

   void vnejsiMetoda() {
      System.out.println("Vnější Metoda - " + vnejsiAtribut);
   }
}
Soubor Vnejsi.jad
import java.io.PrintStream;

class Vnejsi {
    public Vnejsi(int i) {
        vnejsiAtribut = i;
    }

    void vnejsiMetoda() {
        System.out.println("Vn\u011Bj\u0161\355 Metoda - " + vnejsiAtribut);
    }

    public int vnejsiAtribut;
}

Dědičnost

Příklad ukazuje tři třídy, které se navzájem volají. Třídy jsou uloženy v souborech Hlavni.java, Rodic.java, Potomek.java.

Pomocí příkazu javac Hlavni.java se vytvoří soubory Hlavni.class, Rodic.class, Potomek.class.

Zadáním příkazu jad Hlavni.class Rodic.class Potomek.class vzniknou výstupní soubory Hlavni.jad, Rodic.jad, Potomek.jad.

Soubor Rodic.java
public class Rodic {
   public int i;
   public Rodic(int i) { 
      this.i = i; 
   }
   public int getI() { 
      return i; 
   }
}
    Soubor Rodic.jad
public class Rodic {
    public Rodic(int j) {
        i = j;
    }
    public int getI() {
        return i;
    }
    public int i;
}
Soubor Potomek.java
public class Potomek extends Rodic {
   public int k;
   public Potomek(int i, int k) {
     super(i);
     this.k = k;
   }
   public int getK() { 
      return k; 
   }  
}
    Soubor Potomek.jad
public class Potomek extends Rodic {
    public Potomek(int i, int j) {
        super(i);
        k = j;
    }
    public int getK() {
        return k;
    }
    public int k;
}
Soubor Hlavni.java
public class Hlavni {
   public static void main(String[] args){
        Rodic r = new Rodic(1);
        Potomek p = new Potomek(2,3);

        System.out.println(r.getI());

        System.out.println(p.getI());
        System.out.println(p.getK());
   }
}
    Soubor Hlavni.jad
import java.io.PrintStream;

public class Hlavni {
    public Hlavni() {
    }
    public static void main(String args[]) {
        Rodic rodic = new Rodic(1);
        Potomek potomek = new Potomek(2, 3);
        System.out.println(rodic.getI());
        System.out.println(potomek.getI());
        System.out.println(potomek.getK());
    }
}

Program Jad dokáže celkem spolehlivě zrekonstruovat původní kód. Zrekonstruovaný kód se liší od původního v několika detailech, jako např. se změní pořadí deklarací atributů a metod, vnitřní anonymní třídy jsou umístěny do konstruktoru vnější třídy, akcentované znaky jsou nahrazeny buď zápisem hodnoty v Unicode nebo oktalově, názvy lokálních proměnných nebo formálních parametrů jsou pozměněny atp.
I přes tyto malé změny je výsledný zdrojový kód velmi dobře čitelný (možná někdy lépe, než původní zdrojový kód).

Poznámka P.Herouta:
V dokumentaci k Jad se dokonce žertem píše, že by program mohl být použit pro zčitelnění zdrojového kódu. Po zkušenostech z výuky je třeba říci, že výstup z Jad je mnohem lépe čitelný, než mnoho "nedbale" napsaných zdrojových programů. Je možné říci, že programátorovi, který naprosto nedbá na "štábní kulturu" může tento program také výrazně zpřehlednit jeho zdrojové kódy.


Domovské stránky Jad

Pro více informací navštivte domovské stránky dekompilátoru Jad, kde si můžete stáhnout jeho nejnovější verzi:
http://kpdus.tripod.com/jad.html