Help us understand the problem. What is going on with this article?

AndroidのAPKを逆コンパイルする

More than 5 years have passed since last update.

2年ほど前に知ってて当たり前のように教えていただきました。
最近、再び逆コンパイル(デコンパイル)する機会がございましたので、Qiita記事としてメモを投稿しておこうと思います。

手順

  1. apkファイル から dexファイル を取り出す
  2. dexファイルjarファイル に変換する
  3. jarファイル から classファイル を取り出す
  4. classファイルjavaファイル に変換する

だいたいこんな感じです。

必要なツール

逆コンパイルする

まずは、サンプル用のapkを用意します。
せっかくなので以下の記事で使用したサンプルアプリのapkを逆コンパイルしてみようと思います。
ActivityとFragmentのライフサイクルと罠

GitHubへのリンクは↓ですね。
https://github.com/chibi929/AndroidLifeCycle

プロジェクト構成はこんな感じです。
20150930220520.png

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ファイル を見てみます。

本物のMainActivity.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);
            }
        });
    }
}
逆コンパイル後のMainActivity.java
// 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文になっていたりします。

以上。メモがてらの逆コンパイルでした。

mashandroom
会社外活動・コミュニティとして、ハッカソンや各種イベントのボランティアなどに参加。ただただ自分たちが楽しみ、結果周りの人が喜んでくれるようなアウトプットを目指しています。創造せよ!頭にキノコが生えるまで
https://mashandroom.org
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away