by shigemk2

当面は技術的なことしか書かない

FedoraでScalaのクラスファイルを逆コンパイルするまでの軌跡

タイトルのとおりですが、ガリガリやっていきました。

アジェンダのようなもの

  1. 環境
  2. クラスファイルから逆アセンブルする
  3. 逆アセンブル結果から逆コンパイルするためのツールを用意する
  4. 逆コンパイルする

環境

  • Fedora20
  • Scala 2.11.6

何の変哲もありません。

まずはクラスファイルを逆アセンブルする。

gist.github.com

ここでのポイントは、以下の2点です。

  • javap -c -v -pをすること。
  • ファイル名に$のついたクラスファイルを逆アセンブルすること。普通の名前のクラスファイルだとUTF-7として逆アセンブルされるらしい。

逆アセンブル結果から逆コンパイルするためのツールを用意する

わぁい、CUI しげちゃんCUI大好き

JAD

おそらく、CUIのJavaデコンパイラの唯一のツールです。。。 開発終了しているので以下からダウンロードしようとしました。

JAD Java Decompiler Download Mirror

が、jadを実行しようとするといろいろトラブルに見舞われたので、やめました。

出来ればCUIでどうにかしたかったのですが、いかんせんJavaなので、GUIのほうが豊富なようで、CUIの頼みの綱だったJADがダメっぽいので早々に諦めました。論点はそこではないので。

jd-gui

最初は直にバイナリをダウンロードしようと思いましたが、さっきと同じ轍を踏みたくなかったので、直接GitHubからリポジトリをcloneしてどうにかしました。

github.com

が、gradleが入っていないので、まずgradleを入れるところから。。

gradleを入れよう

ちょっと参考にしました。gradle is 何ってところは割愛します。

www.atmarkit.co.jp

  • バイナリダウンロード http://gradle.org/downloads/
  • 解凍したやつを適当なところに置く。
  • .zshrcとか.bashrcとかに以下のように適当に書く。
export GRADLE_HOME=$HOME/gradle
export PATH=$GRADLE_HOME/bin:$PATH

.zshrcなどを読みこめば、とりあえずgradleが使えます。

あとは、ビルドして、jarファイルを実行するとJD-GUIが動きます。

$ gradle build 
$ java -jar build/libs/jd-gui-1.1.0.jar

逆コンパイルしてみよう

JD-GUIからファイルを開くと自動的に逆コンパイルしてくれます。

動いたやつがこちら。

f:id:shigemk2:20150524004418p:plain

逆コンパイル結果がこちら。

gist.github.com

なお、Scalaのクラスファイルの逆コンパイルは非情に使い古されたネタです。残念なことに。

もっというと、この逆コンパイルしたJavaのソースコードをjavacコンパイルして動かそうとしてもダメでした。本当に逆コンパイルするだけです。

ScalaのクラスファイルをJavaで実行したければ別のアプローチが必要となります。

www.shigemk2.com

こちらからは以上です。

クラスファイルの LineNumberTableとLocalVariableTableについて

こいつをコンパイルして逆アセンブルしてみました。

gist.github.com

逆アセンブル結果がこちら。

  public void main(java.lang.String[]);
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=2
         0: iconst_1      
         1: istore_2      
         2: iload_2       
         3: iconst_2      
         4: iadd          
         5: istore_2      
         6: return        
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       7     0  this   LPlusScala$;
               0       7     1  args   [Ljava/lang/String;
               2       4     2     a   I
      LineNumberTable:
        line 3: 0
        line 4: 2

LocalVaribleTableとLineNumberTableってなんぞ。っていうはなし。

www.s-cradle.com

LineNumberTable はそのバイトコードとソース上の行番号の対応がかかれています。LocalVariableTable は、メソッド内で宣言した変数の名前などを保持したものです。 LineNumberTable と LocalVariableTable は主にデバッグ時に用いられ、実行時には不必要な属性です。

という説明なので、実際に読むときは必要のないものだったりするようです。

確かに、仕様書を読むとどちらもデバッガが見る情報みたいですね。

今日やったこと #ikebin

やったこと。

github.com

ScalaとJavaのクラスファイルについてごにょごにょしていました。以下雑多なメモ。

  • javac -gでデバッグ情報を出しつつコンパイル。
  • なお、scalacは普通にデバッグ情報を出してくれるけど、詳しく調べなければならない。
  • Scalaの関数にstaticはないので、JavaとScalaのデバッグ情報にズレが生じやすい。
  • デバッグ情報とか無視したらトレイトもインターフェイスも一緒の逆アセンブル結果になる。
  • ScalaのクラスファイルをJD-GUIで逆コンパイルしたらわけがわからないやつが出てくる。

シングルトン

gist.github.com

ツイート