2年ほど前に知ってて当たり前のように教えていただきました。
最近、再び逆コンパイル(デコンパイル)する機会がございましたので、Qiita記事としてメモを投稿しておこうと思います。
手順
-
apkファイル
からdexファイル
を取り出す -
dexファイル
をjarファイル
に変換する -
jarファイル
からclassファイル
を取り出す -
classファイル
をjavaファイル
に変換する
だいたいこんな感じです。
必要なツール
- dex2jar(手順2で使用する)
- Java Decompiler(手順4で使用する)
逆コンパイルする
まずは、サンプル用のapkを用意します。
せっかくなので以下の記事で使用したサンプルアプリのapkを逆コンパイルしてみようと思います。
ActivityとFragmentのライフサイクルと罠
GitHubへのリンクは↓ですね。
https://github.com/chibi929/AndroidLifeCycle
apkファイル から dexファイル を取り出す
apkファイルはzipなので unzip
で解凍ができます。
解凍前の状況がこちら
$ ls
app-debug.apk
unzipコマンドの様子がこちら
$ unzip app-debug.apk
Archive: app-debug.apk
inflating: AndroidManifest.xml
inflating: res/anim/abc_fade_in.xml
~略~
unzipで解凍後の状況がこちら
$ ls
AndroidManifest.xml classes.dex META-INF res resources.arsc
classes.dex
が取り出せました。
dexファイル を jarファイル に変換する
ここで dex2jarの出番です。
今回は記事投稿時点で最新版だった dex2jar-2.0
を使います。
dex2jarのスクリプトファイルに実行権限がなかったので付けました。
$ chmod +x -R dex2jar-2.0
dex2jar実行の様子がこちら
$ ~/tool/dex2jar-2.0/d2j-dex2jar.sh classes.dex
dex2jar classes.dex -> ./classes-dex2jar.jar
dex2jar実行後の状況がこちら
$ ls
AndroidManifest.xml app-debug.apk classes.dex classes-dex2jar.jar META-INF res resources.arsc
classes-dex2jar.jar
が増えています。
jarファイル から classファイル を取り出す
jarファイルはご存知の通りzipですので、apkファイル同様unzipをします。
unzipコマンドの様子がこちら
$ unzip classes-dex2jar.jar
Archive: classes-dex2jar.jar
creating: android/
creating: android/support/
~略~
unzipで解凍後の状況がこちら
$ ls
android AndroidManifest.xml app-debug.apk chibi classes.dex classes-dex2jar.jar META-INF res resources.arsc
androidディレクトリ
と chibiディレクトリ
が生まれています。
chibiディレクトリ
は以下のような構成になっております。
chibi/jp/lifecycle
│ AppActivity.class
│ AppFragment.class
│ BuildConfig.class
│ MainActivity$1.class
│ MainActivity$2.class
│ MainActivity.class
│ R$anim.class
│ R$attr.class
│ R$bool.class
│ R$color.class
│ R$dimen.class
│ R$drawable.class
│ R$id.class
│ R$integer.class
│ R$layout.class
│ R$menu.class
│ R$mipmap.class
│ R$string.class
│ R$style.class
│ R$styleable.class
│ R.class
│ V4AppActivity.class
│ V4AppFragment.class
│
└─base
LifeCycleActivity.class
LifeCycleFragment.class
LifeCycleFragmentActivity.class
LifeCycleV4Fragment.class
一番最初に載せたプロジェクト構成の画像とほぼ同じですね。
classファイル を javaファイル に変換する
最後に Java Decompiler の出番です。
$ ~/tool/jad -o -r -sjava -ddecomp chibi/**/*.class
Parsing chibi/jp/lifecycle/BuildConfig.class...The class file version is 50.0 (only 45.3, 46.0 and 47.0 are supported)
Generating decomp/chibi/jp/lifecycle/BuildConfig.java
Parsing chibi/jp/lifecycle/R.class...The class file version is 50.0 (only 45.3, 46.0 and 47.0 are supported)
~略~
コマンドは Java Decompiler の Readme.txt に書いてあったものをそのまま・・・。
それでは decompディレクトリ
のディレクトリ構成を確認します。
decomp
└─chibi
└─jp
└─lifecycle
│ AppActivity.java
│ AppFragment.java
│ BuildConfig.java
│ MainActivity.java
│ R.java
│ V4AppActivity.java
│ V4AppFragment.java
│
└─base
LifeCycleActivity.java
LifeCycleFragment.java
LifeCycleFragmentActivity.java
LifeCycleV4Fragment.java
もう、ほぼ元通りです。
最後に
本物のjavaファイル
と 逆コンパイル後のjavaファイル
を見てみます。
package chibi.jp.lifecycle;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.appFragmentButton).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(getApplicationContext(), AppActivity.class);
startActivity(i);
}
});
findViewById(R.id.v4AppFragmentButton).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(getApplicationContext(), V4AppActivity.class);
startActivity(i);
}
});
}
}
// Decompiled by Jad v1.5.8e. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.geocities.com/kpdus/jad.html
// Decompiler options: packimports(3)
package chibi.jp.lifecycle;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
// Referenced classes of package chibi.jp.lifecycle:
// AppActivity, V4AppActivity
public class MainActivity extends AppCompatActivity
{
public MainActivity()
{
}
protected void onCreate(Bundle bundle)
{
super.onCreate(bundle);
setContentView(0x7f04001a);
findViewById(0x7f0c0050).setOnClickListener(new android.view.View.OnClickListener() {
public void onClick(View view)
{
view = new Intent(getApplicationContext(), chibi/jp/lifecycle/AppActivity);
startActivity(view);
}
final MainActivity this$0;
{
this$0 = MainActivity.this;
super();
}
});
findViewById(0x7f0c0051).setOnClickListener(new android.view.View.OnClickListener() {
public void onClick(View view)
{
view = new Intent(getApplicationContext(), chibi/jp/lifecycle/V4AppActivity);
startActivity(view);
}
final MainActivity this$0;
{
this$0 = MainActivity.this;
super();
}
});
}
}
MainActivity.javaにはif文が無かったのですが、
if文があると逆コンパイル後のjavaファイルにはgoto文になっていたりします。
以上。メモがてらの逆コンパイルでした。