APK反編譯後插入調試代碼動態運行

1. 引入

當我們拿到一個APK,沒有源代碼,該怎麼樣去研究APK的核心邏輯呢?

限於運行環境的複雜,我們會首先使用靜態分析的方式,大概可以想出這樣一些靜態分析APK的方法:

  1. 用apktool直接將APK轉換爲smali程序,再閱讀smali代碼(比較痛苦)

  2. 用dextojar將APK中的DEX轉換爲jar,再用JD-JUI來查看其java代碼

  3. 用JEB,直接查看java或smali(JEB是收費軟件,比較貴)

當我們做這樣的靜態分析,定位到某些關鍵邏輯,就要用動態調試的手段來觀測某些變量的值了。

這時候,我們就會在APK中,關鍵邏輯的地方,插入LOG,然後運行觀測,這個過程就是本文要講述的內容。

本文的實驗環境如下:

  • windows 10
  • apktool_2.4.0.jar
  • java version “1.8.0_201”

2. 要分析的APK

我們自己寫了一個很簡單的APK(文件名爲hello-apk.apk),後面稱之爲hello-apk,專門用於反編譯分析。分析其他複雜APK的過程也和本文講述的過程是一樣的,只是用這個簡單hello-apk,更能清晰、簡潔、易懂的說明這個過程。

hello-apk的界面很簡單,就是一個button,加上一個textview。當我們單擊button,就會在textview上顯示"Hello world! click by button!!"。其核心邏輯見下面代碼


public class MainActivity extends AppCompatActivity {

    private int count=0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void btn1ClickEvent(View target){
        count++;
        TextView txt=(TextView)findViewById(R.id.mytextview);//find output label by id
        txt.setText("Hello world! click by button!!");
    }
}

這裏的關鍵邏輯,就是函數btn1ClickEvent()中的內容,它負責處理button的click事件。

接下來我們講解如何查看count值的過程。

3. 安裝配置apktool的步驟

  1. 具體步驟參考: https://ibotpeaches.github.io/Apktool/install/

  2. 將如下鏈接中顯示的腳本內容保存爲apktool.bat

  • https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/windows/apktool.bat
  1. 到如下鏈接下載最新版本的apktool
  • https://bitbucket.org/iBotPeaches/apktool/downloads/
  1. 將下載的apktool重命名爲apktool.jar,並與apktool.bat放在同一個文件夾,添加環境變量

本文將hello-apk與apktool.jarapktool.bat放在同一個文件夾,所以省去了添加環境變量的步驟。

4. 用apktool反編譯APK爲smali

使用如下命令(apktool d apkfile)來反編譯apk

>apktool d hello-apk.apk

解包執行結束後,我們在當前目錄得到hello-apk文件夾,裏面就是從APK中反編譯出來的smali代碼,資源文件,與manifest文件。

5. 插入調試用到的smali代碼

在解包得到的smali文件夾中,我們可以找到需要添加LOG的smali文件,位於hello-apk/smali/com/example/ybdesire/hello/MainActivity.smali

關鍵函數btn1ClickEvent()的smali代碼如下,可以對比上面的純java代碼,這就是java編譯後的smali:

# virtual methods
.method public btn1ClickEvent(Landroid/view/View;)V
    .locals 2#這個函數中,使用了2個局部變量,就是下面的v0,v1
    .param p1, "target"    # Landroid/view/View;# 函數形參

    .line 19#將count變量++
    iget v0, p0, Lcom/example/ybdesire/hello/MainActivity;->count:I

    add-int/lit8 v0, v0, 0x1

    iput v0, p0, Lcom/example/ybdesire/hello/MainActivity;->count:I

    .line 20#找到textview的id
    const v0, 0x7f070052

    invoke-virtual {p0, v0}, Lcom/example/ybdesire/hello/MainActivity;->findViewById(I)Landroid/view/View;

    move-result-object v0

    check-cast v0, Landroid/widget/TextView;

    .line 21#將字符串顯示到textview上
    .local v0, "txt":Landroid/widget/TextView;
    const-string v1, "Hello world! click by button!!"

    invoke-virtual {v0, v1}, Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;)V

    .line 23
    return-void
.end method

我們想在上面程序的最後,插入一行java程序的smali代碼,這樣我們就能在程序運行時,通過log輸出其局部變量的值,也就是能夠動態查看中間結果了。

Log.d("btn_debug", "count="+count);//這是想插入的java程序

這一行java程序對應的smali程序如下:

    .line 22
    const-string v1, "btn_debug"

    new-instance v2, Ljava/lang/StringBuilder;

    invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V

    const-string v3, "count="

    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    # 通過閱讀上面的smali,可以發現,count變量的值會被放到p0
    iget v3, p0, Lcom/example/ybdesire/hello/MainActivity;->count:I

    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;

    invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;

    move-result-object v2

    invoke-static {v1, v2}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

接下來我們來修改smali:

  1. 將這段smali程序,插入到hello-apk/smali/com/example/ybdesire/hello/MainActivity.smali.line 23上面。

  2. .locals 2改爲.locals 4,因爲上面插入的smali代碼,還使用了v2與v3。

最終的smali如下:

# virtual methods
.method public btn1ClickEvent(Landroid/view/View;)V
    .locals 4
    .param p1, "target"    # Landroid/view/View;

    .line 19
    iget v0, p0, Lcom/example/ybdesire/hello/MainActivity;->count:I

    add-int/lit8 v0, v0, 0x1

    iput v0, p0, Lcom/example/ybdesire/hello/MainActivity;->count:I

    .line 20
    const v0, 0x7f070052

    invoke-virtual {p0, v0}, Lcom/example/ybdesire/hello/MainActivity;->findViewById(I)Landroid/view/View;

    move-result-object v0

    check-cast v0, Landroid/widget/TextView;

    .line 21
    .local v0, "txt":Landroid/widget/TextView;
    const-string v1, "Hello world! click by button!!"

    invoke-virtual {v0, v1}, Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;)V
    
    .line 22
    const-string v1, "btn_debug"

    new-instance v2, Ljava/lang/StringBuilder;

    invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V

    const-string v3, "count="

    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    iget v3, p0, Lcom/example/ybdesire/hello/MainActivity;->count:I

    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;

    invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;

    move-result-object v2

    invoke-static {v1, v2}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

    .line 23
    return-void
.end method

通過這樣修改smali,我們最終得到的程序邏輯,用java表示如下:

    public void btn1ClickEvent(View target){
        count++;
        TextView txt=(TextView)findViewById(R.id.mytextview);//find output label by id
        txt.setText("Hello world! click by button!!");
        Log.d("btn_debug", "my count="+count);
    }

6. 改動後的smali打包爲APK

使用如下命令(apktool d dir_name)來將改動後的smali,打包爲APK文件

>apktool b hello-apk

打包完成後,可以在hello-apk/dist中看到打包出來的APK文件。

7. 爲APK簽名

apktool打包出來的APK,缺少簽名信息,無法在android設備安裝運行,必須要重新簽名(詳細步驟參考[1])。

  1. 生成簽名文件

運行如下命令

keytool -genkey -alias abc.keystore -keyalg RSA -validity 20000 -keystore abc.keystore

需要輸入簽名的密碼與其他信息,下面用中文說明具體要輸入的內容(括號中是說明,不是真實輸入的內容):

hello-apk\dist>keytool -genkey -alias abc.keystore -keyalg RSA -validity 20000 -keystore abc.keystore
Enter keystore password:(輸入密碼)
Re-enter new password:(再次輸入密碼)
What is your first and last name?
  [Unknown]:  a
What is the name of your organizational unit?
  [Unknown]:  a
What is the name of your organization?
  [Unknown]:  a
What is the name of your City or Locality?
  [Unknown]:  a
What is the name of your State or Province?
  [Unknown]:  a
What is the two-letter country code for this unit?
  [Unknown]:  a
Is CN=a, OU=a, O=a, L=a, ST=a, C=a correct?
  [no]:  yes

Enter key password for <abc.keystore>
        (RETURN if same as keystore password):(再次輸入密碼)
Re-enter new password:(再次輸入密碼)

Warning:
The JKS keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format using "keytool -importkeystore -srckeystore abc.keystore -destkeystore abc.keystore -deststoretype pkcs12".

最終會生成一個abc.keystore證書文件。

  1. 爲apk簽名

運行如下命令,對hello-apk.apk簽名,簽名後生成的文件爲hello-apk-s.apk

jarsigner -verbose -keystore abc.keystore -signedjar hello-apk-s.apk hello-apk.apk abc.keystore

運行後,需要輸入上面爲簽名設置的密碼。

Enter Passphrase for keystore:
   adding: META-INF/MANIFEST.MF
   adding: META-INF/ABC_KEYS.SF
   adding: META-INF/ABC_KEYS.RSA
  signing: AndroidManifest.xml
  signing: classes.dex
  signing: res/anim/abc_fade_in.xml
  signing: res/anim/abc_fade_out.xml
  signing: res/anim/abc_grow_fade_in_from_bottom.xml
  signing: resources.arsc
>>> Signer
    X.509, CN=a, OU=a, O=a, L=a, ST=a, C=a
    [trusted certificate]

jar signed.

Warning:
The signer's certificate is self-signed.

最終我們得到簽名後的hello-apk-s.apk文件。

8. 查看調試信息

簽名後的hello-apk-s.apk文件,可以直接安裝在android設備上。

運行APP,用adb查看log,如下命令可以只查看tag爲btn_debug的debug級別的log

adb logcat btn_debug:D

多次點擊APP的button,可以得到如下log

09-01 16:33:41.863  1700  1700 I Zygote  : Process 4887 exited due to signal (9)
09-01 16:33:42.057  3346  3346 D btn_debug: count=7
09-01 16:33:42.837  3346  3346 D btn_debug: count=8
09-01 16:33:42.993  3346  3346 D btn_debug: count=9
09-01 16:33:43.191  3346  3346 D btn_debug: count=10

這就實現了把APK中的局部變量count的值作爲輸出。

9. 總結

使用apktool,可以把APK解包,我們就能在解包後的smali中插入調試APK需要的代碼,再將改動後的smali代碼打包爲APK,簽名運行後,就能用adb看到我們插入smali代碼的輸出了。

10. 參考

  • [1] https://blog.csdn.net/ybdesire/article/details/52505648
發佈了190 篇原創文章 · 獲贊 259 · 訪問量 122萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章