Android逆向分析之dex2jar和jd-gui使用

從大三伊始到大四落幕,從剛開始接觸Android到辭掉第一份實習工作,我接觸Android應用層開發也快接近兩年了。越來越發覺Android的應用層已經沒什麼挑戰性了,想當初剛開始學習Android的時候,弄了一個Activity出來顯示在手機的那份喜悅,真是~哈哈~,應用層的開發無非也就調用JDK,SDK而已,現在感覺有點小兒科啊,實習期間,每當工作項目之餘,基本都泡到自定義View的繪製去,那也是我所能解悶的工作了。可是,這並不符合的職業規劃,我想往前發展,比如framework層或者其他深層的技術等。

因爲實習將近一年,在Android開發方面有一定的經驗,再加上自己是應屆生,所以有家稍微有點大但名氣比較低(起碼叫我面試的時候我都沒聽過這公司,額。。)的上市公司給我伸出了橄欖枝,從白紙開始培養人才。又因爲提供的崗位叫Android逆向分析工程師,以前就聽過這霸氣的名字了,實際就是白帽子的工作,所以我也簽了這公司,來實習了,畢竟還有一個月才能拿到畢業證。


說實話,逆向分析已經和Android應用開發不是一個level了,也和應用層開發沒什麼關係了,只是逆向分析需要熟悉應用層開發中的內容而已,比如反編譯後要找到某個Activity或着fragment,總之,你要定位到關鍵代碼,那你就必須得熟悉Android應用層的開發內容,尤其是混淆過,那就更需要熟悉開發的結構了,不然要在一大堆反編譯的文件中找到你要的代碼簡直就令人發怵。

嗯,廢話不多說,既然沒有什麼經驗,那麼就好好學習。先來個入門的工作,就是利用dex2jar反編譯一個APK,並用jd-gui.exe查看jar包內容。

第一步,先寫個簡單的工程,並簽名打包導出APK。

LoginActivity.java:

public class LoginActivity extends Activity {

    private final String ACCOUNT="samuel";
    private final String PASSWORD="123456";
    private EditText etAccount, etPassword;
    private Button btnLogin;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        etAccount=(EditText)findViewById(R.id.et_account);

        etPassword=(EditText)findViewById(R.id.et_password);

        btnLogin=(Button)findViewById(R.id.btn_login);

        btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isOK(etAccount.getText().toString(), etPassword.getText().toString())) {
                    Toast.makeText(LoginActivity.this, "登錄成功", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(LoginActivity.this, "登錄失敗", Toast.LENGTH_SHORT).show();
                }

            }
        });

    }

    private boolean isOK(String account, String password){
        return account.equals(ACCOUNT) && password.equals(PASSWORD);
    }

}

佈局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_centerInParent="true">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:orientation="horizontal">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="帳號:"/>

            <EditText
                android:id="@+id/et_account"
                android:layout_width="100dp"
                android:layout_height="wrap_content" />

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:orientation="horizontal">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="密碼:"/>

            <EditText
                android:id="@+id/et_password"
                android:layout_width="100dp"
                android:layout_height="wrap_content" />

        </LinearLayout>

        <Button
            android:id="@+id/btn_login"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="登錄"/>

    </LinearLayout>

</RelativeLayout>

簽名打包 Build>Generate Signed APK:



第二步,將導出的xxx.apk文件的後綴.apk改爲.zip,即壓縮文件後綴,然後解壓。(高版本的dex2jar貌似是不需要2,3步的,直接把APK扔到dos裏,直接輸入命令dex2jar xxx.apk即可)


第三步,下載dex2jar,用cmd打開dos系統,並在dex2jar所在窗口的dos位置輸入  dex2jar  路徑名+classes.dex,即在上圖的文件夾中(不帶路徑,則默認在當前文件夾)生成一個.jar後綴的名字的文件。



第四步,jar文件是不可以直接看的,要用到配套的工具jd-gui.exe打開。



至此,反編譯的入門工作也差不多了。可以看到,由於APK打包時沒有作混淆處理,被反編譯過來後,其代碼的類名和成員變量名字都是沒有變化的,這很容易讓反編譯的人看到源碼,並實施惡意行爲,所以在打包的時候必須要作混淆處理。

最後,我們比較一下不做和做了混淆處理後,同樣經過以上步驟反編譯出來的jar文件是怎樣的:

不做混淆的反編譯代碼:

LoginActivity.java:

public class LoginActivity extends Activity
{
  private final String ACCOUNT = "samuel";
  private final String PASSWORD = "123456";
  private Button btnLogin;
  private EditText etAccount;
  private EditText etPassword;

  private boolean isOK(String paramString1, String paramString2)
  {
    return (paramString1.equals("samuel")) && (paramString2.equals("123456"));
  }

  protected void onCreate(Bundle paramBundle)
  {
    super.onCreate(paramBundle);
    setContentView(2130968601);
    this.etAccount = ((EditText)findViewById(2131492944));
    this.etPassword = ((EditText)findViewById(2131492945));
    this.btnLogin = ((Button)findViewById(2131492946));
    this.btnLogin.setOnClickListener(new View.OnClickListener()
    {
      public void onClick(View paramAnonymousView)
      {
        if (LoginActivity.this.isOK(LoginActivity.this.etAccount.getText().toString(), LoginActivity.this.etPassword.getText().toString()))
        {
          Toast.makeText(LoginActivity.this, "登錄成功", 0).show();
          return;
        }
        Toast.makeText(LoginActivity.this, "登錄失敗", 0).show();
      }
    });
  }
}

經過混淆處理後的反編譯代碼:

LoginActivity.java:

public class LoginActivity extends Activity
{
  private final String a = "samuel";
  private final String b = "123456";
  private EditText c;
  private EditText d;
  private Button e;

  private boolean a(String paramString1, String paramString2)
  {
    return (paramString1.equals("samuel")) && (paramString2.equals("123456"));
  }

  protected void onCreate(Bundle paramBundle)
  {
    super.onCreate(paramBundle);
    setContentView(2130968601);
    this.c = ((EditText)findViewById(2131492944));
    this.d = ((EditText)findViewById(2131492945));
    this.e = ((Button)findViewById(2131492946));
    this.e.setOnClickListener(new a(this));
  }
}

a.java:

class a
  implements View.OnClickListener
{
  a(LoginActivity paramLoginActivity)
  {
  }

  public void onClick(View paramView)
  {
    if (LoginActivity.a(this.a, LoginActivity.a(this.a).getText().toString(), LoginActivity.b(this.a).getText().toString()))
    {
      Toast.makeText(this.a, "登錄成功", 0).show();
      return;
    }
    Toast.makeText(this.a, "登錄失敗", 0).show();
  }
}

比較可看到,混淆過的代碼中,關鍵的變量名都被a, b, c, d等字母代替了,其中button的匿名內部監聽類由View.OnClickListener類變成了a類。這樣一來,混淆過的代碼就沒有那麼容易受到惡意攻擊了。但貌似還是有辦法反混淆的,所以也沒有百分百的安全吧。


反編譯工具下載:dex2jar & jd-gui


發佈了33 篇原創文章 · 獲贊 50 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章