`

反编译并且修改Android APK包

阅读更多

為了某個實驗的動機,我們評估反編譯 Android 應用程式的可行性,本文即是筆者的心得與實際的範例,僅供參考。就筆者的認知,目前還沒有針對 Android 的 DEX to Java source 反編譯工具,可實際處理一般的 Android 應用程式,多半要繞幾圈,還會得到不甚理想的結果,不過,smali 這個反組譯工具,已是可用了,只是得對付類似 Jasmin 語法的 Dalvik 組合語言。筆者打包了 smaliFrozen Bubble for Android,作為示範:

在 GNU/Linux 環境中,首先取得 Android SDK,這裡採用 Eclair/2.1,工具執行檔的路徑是 android-sdk-linux_86/tools,將此路徑放入 $PATH 環境變數,如此一來,就可以操作 adb, aapt, apkbuilder 一類的工具程式。筆者提供的套件已包含 Frozen Bubble 執行檔,檔名為 "FrozenBubble-orig.apk"。一旦 Android emulator 啟動後,即可安裝進去執行:(後續的操作也需要 Emulator 持續開啟)
$ adb install -r FrozenBubble-orig.apk
以下是執行畫面:




 

當然,沒必要讓筆者置喙談如何玩這個經典遊戲,不過我們倒是想更改原本的行為。在進行之前,我們先來複習 Android APK 的建立,參考 "How to build Android application package (.apk) from the command line using the SDK tools + continuously integrated using CruiseControl." 一文,我們可從以下圖表知悉細部的流程:




 


假設我們完全無法取得原始程式碼,該如何進行呢?沒錯,就透過 smali,簡化繁瑣的流程,筆者包裝為 Makefile,所以只要先解開並反組譯:
$ make extract
這時候會看到兩個目錄:
  • smali-src : 存放反組譯的程式檔輸出
  • workspace : 原本 Frozen Bubble 的 Android resource files
二話不說,當然是觀察反組譯的結果:
smali-src$ find
./org/jfedor/frozenbubble/FrozenBubble.smali
./org/jfedor/frozenbubble/R$id.smali
./org/jfedor/frozenbubble/GameView.smali
./org/jfedor/frozenbubble/SoundManager.smali
./org/jfedor/frozenbubble/LaunchBubbleSprite.smali
./org/jfedor/frozenbubble/Compressor.smali
./org/jfedor/frozenbubble/R$attr.smali
./org/jfedor/frozenbubble/BubbleFont.smali
./org/jfedor/frozenbubble/PenguinSprite.smali
./org/jfedor/frozenbubble/GameView$GameThread.smali
./org/jfedor/frozenbubble/BubbleSprite.smali./org/jfedor/frozenbubble/R$string.smali
./org/jfedor/frozenbubble/R$drawable.smali
./org/jfedor/frozenbubble/ImageSprite.smali
./org/jfedor/frozenbubble/BubbleManager.smali
./org/jfedor/frozenbubble/GameScreen.smali
./org/jfedor/frozenbubble/R.smali
./org/jfedor/frozenbubble/R$layout.smali
./org/jfedor/frozenbubble/BmpWrap.smali./org/jfedor/frozenbubble/FrozenGame.smali
./org/jfedor/frozenbubble/Sprite.smali
./org/jfedor/frozenbubble/LevelManager.smali
./org/jfedor/frozenbubble/R$raw.smali
就 忠實地依據 Java package 的方式呈現,檔名結尾是 ".smali"。筆者的修改目標是,讓一開始的關卡 (Level) 從第一關直接跳躍到第五關。在 smali 原始程式碼 (注意:這完全不同於 Java 原始程式碼,而是極為貼近 Dalvik VM 所接受的 DEX 檔案的組合語言形式) 搜尋 "Level" 字眼,可發現主要的分佈就落於兩個檔案:
  • ./org/jfedor/frozenbubble/GameView$GameThread.smali
  • ./org/jfedor/frozenbubble/LevelManager.smali
前 者就是 org/jfedor/frozenbubble/GameView.java 的編譯輸出,因為是 inner class,獨立編譯出 org/jfedor/frozenbubble/GameView.class$GameThread.smali,由這個 class 命名方式大概就可猜出其重要性,基本上只要能夠控制此 class 的實做,就掌握此 Android 應用程式的行為。而 class LevelManager 顧名思義,看來掌握了遊戲進行的程序控制。先觀察其 method 列表:
smali-src$ grep "\.method" org/jfedor/frozenbubble/LevelManager.smali
.method public constructor <init>([BI)V
.method private getLevel(Ljava/lang/String;)[[B
.method public getCurrentLevel()[[B
.method public getLevelIndex()I
.method public goToFirstLevel()V
.method public goToNextLevel()V.method public restoreState(Landroid/os/Bundle;)V
.method public saveState(Landroid/os/Bundle;)V
倘若我們以 "goToFirstLevel" 一類的關鍵字,在 org/jfedor/frozenbubble/GameView$GameThread.smali 檔案中搜尋,可找出有具體的呼叫行為:
smali-src$ grep -r goToFirstLevel *
org/jfedor/frozenbubble/GameView$GameThread.smali: invoke-virtual {v2}, Lorg/jfedor/frozenbubble/LevelManager;->goToFirstLevel()V
org/jfedor/frozenbubble/LevelManager.smali:.method public goToFirstLevel()V
由此更確定我們之前的猜想。其中組合語言寫為以下:
move-object/from16 v0, p0

iget-object v0, v0, Lorg/jfedor/frozenbubble/GameView$GameThread;->mLevelManager:Lorg/jfedor/frozenbubble/LevelManager;

move-object v2, v0

invoke-virtual {v2}, Lorg/jfedor/frozenbubble/LevelManager;->goToFirstLevel()V
不 要被貌似複雜的語法嚇到了,基本上掌握 Java 程式語言的原則 "Everything is Object" (不過仍有提供 primitive type),組合語言仍會作 Java Object 的實體化 (instantialization),Dalvik 本身是 Register-based Virtual Machine,而扣除 static/class method 外,Java 中所有的 method invocation 多為 virtual function (對應於 C++ 的觀點,才能更具體用機械方式思考),所以組合語言的指令為 "invoke-virtual" (注意有連字號,此與 Java bytecode 不同),"{v2}" 表示第一受者的 Register,此為 Object 本體。接著,與 Java bytecode 一樣,"Lorg/jfedor/frozenbubble/LevelManager;" 就表示 Java 層面的 class "org.jfedor.frozenbubble.LevelManager",字母 "L" 即為 class 的識別,而 "->" 就很直觀了,自然是 method invocation,所以連貫來看,這一段組合語言的 Java 意思為:
objectLevelManager.goToFirstLevel();
其 中 objectLevelManager 是一個 class LevelManager 的實例/實體 (instance)。倘若需要在 method invocation 時,帶入參數,那麼前述的 "{v2}" 一般會被替換為 "{v0, v1, v2, ...} 的 register 列表。關於詳細的狀況,可參考 Dalvik 非官方說明,另外 smali 的 wiki 也提供一些範例,可多加利用。

回 到筆者剛剛設定的目標,我們既然知道 class org.jfedor.frozenbubble.GameView$GameThread 掌控了程式處理邏輯,自然一堆變數的傳遞、method 呼叫,都在此進行,那我們先試著用 "level" 字串去搜尋,想辦法找出常數定義,後者在 Dalvik 中,會集中保存於 constant pool 中,而 smali 的組合語言寫法大致是 "const" 開頭的宣告,端看其類型而定。以程式追蹤的目的來說,我們專注於以下兩種:
  • const-string : primitive string (不同於 java.lang.String) 表示
  • const/4 : 長度為 4 bytes (32 bit) 的整數表示
字 串何其多,依據之前的推測來說,我們要找接近 "LevelManager" 字眼的組合語言程式碼,道理很明顯,從一般 Java programmer 的寫作邏輯去反推。經過一番搜尋,我們找到以下的反組譯程式碼: (位於檔案 org/jfedor/frozenbubble/GameView$GameThread.smali中)
const-string v3, "level"
const/4 v4, 0x0
move-object/from16 v0, v25 move-object v1, v3
move v2, v4
invoke-interface {v0, v1, v2}, Landroid/content/SharedPreferences;->getInt(Ljava/lang/String;I)I
move-result p4
new-instance v3, Lorg/jfedor/frozenbubble/LevelManager;
move-object v0, v3
move-object/from16 v1, v22
move/from16 v2, p4 invoke-direct {v0, v1, v2}, Lorg/jfedor/frozenbubble/LevelManager;-><init>([BI)V
在 上述程式碼列表中,"Lorg/jfedor/frozenbubble/LevelManager;-><init>" 表示呼叫 class LevelManager 的 constructor,也就是 "<init>"。注意到 method invocation 方式就不同了,是 "invoke-direct",表示 class constructor,而這之前要有 "new-instance v3, Lorg/jfedor/frozenbubble/LevelManager;" 的組合語言指令宣告。

前 面談過「倘若需要在 method invocation 時,帶入參數,一般會被替換為 "{v0, v1, v2, ...} 的 register 列表」這樣的概念,我們可推知,Register v1 與 v2 就是實際上 class org.jfedor.frozenbubble.LevelManager 的 constructor 參數。就程式設計的邏輯來看,class GameView 就是依據某個流程,要求 LevelManager 去改變狀態,所以這裡的兩個參數,其實就是初始值,非常的重要。

與 Register v1 相關的組合語言指令有這幾行:(用粗體字標示)
const-string v3, "level"
const/4 v4, 0x0
move-object/from16 v0, v25
move-object v1, v3
move v2, v4
invoke-interface {v0, v1, v2}, Landroid/content/SharedPreferences;->getInt(Ljava/lang/String;I)I
move-result p4
new-instance v3, Lorg/jfedor/frozenbubble/LevelManager;
move-object v0, v3
move-object/from16 v1, v22
move/from16 v2, p4
invoke-direct {v0, v1, v2}, Lorg/jfedor/frozenbubble/LevelManager;-><init>([BI)V
顯 然,Register v1 還被帶入到 android.content.Shared.Preference.getInt() method,而更早以前,其內含值被設定為 Register v3 的值,也就是常數字串 (const-string) "level",這好像與我們的焦點不同。另外,像是 Register v22 這個編號較大的 register,表示 local variable,這點需要多留意,因為 Java 程式設計的規範來說,往往會將程式切割為若干 method,而 method 實做體中,又有極多的 local variable,於是往往可從組合語言反推 Java 原始碼的型態。

那麼,看看 Register v2 吧,同樣用粗體字標示相關的指令:
const-string v3, "level"
const/4 v4, 0x0
move-object/from16 v0, v25
move-object v1, v3
move v2, v4
invoke-interface {v0, v1, v2}, Landroid/content/SharedPreferences;->getInt(Ljava/lang/String;I)I
move-result p4
new-instance v3, Lorg/jfedor/frozenbubble/LevelManager;
move-object v0, v3
move-object/from16 v1, v22
move/from16 v2, p4
invoke-direct {v0, v1, v2}, Lorg/jfedor/frozenbubble/LevelManager;-><init>([BI)V
這 個 Register v4 的內含值 "0x0" 會指派到 Register v2 中,而讓我們似乎找到方向了,回頭看看 class org.jfedor.frozenbubble.LevelManager 的 constructor 宣告方式: (之前 grep 結果的第一行)
smali-src$ grep "\.method" org/jfedor/frozenbubble/LevelManager.smali
.method public constructor <init>([BI)V
其中 "public" 是 ACL (存取權限) 的宣告,而 constructor 的符號規範為 "<init>",注意到括號 "(" 與 ")" 裡面的兩個大寫字母,表示接受兩個參數,對應的型態為:
  • B : byte
  • I : int
在 Java 與 Dalvik virtual machine 皆以同樣的形式作宣告,看到這裡,我們實在忍不住要動手修改了,當然,一切都是實驗性質。既然一開啟遊戲,畫面就顯示 Level 1,表示 LevelManager 一開始接受的 level 參數會是 "0",然後依據 GameView 的邏輯,逐一升級或中斷遊戲進行,那麼,如果要讓遊戲一起動就是 Level 5,是不是要把內含值改為 0x4 即可?也就是 "invoke-direct {v0, v1, v2}, Lorg/jfedor/frozenbubble/LevelManager;-><init>([BI)V" 的 Register v2 當時內含值該為 0x4,不就任務達成了嗎?想到此,不禁會心一笑。

不過,回顧稍早 Register v2 的相關程式碼輸出,其中有兩行需要留意 (以粗體字為主):
invoke-interface {v0, v1, v2}, Landroid/content/SharedPreferences;->getInt(Ljava/lang/String;I)I
move-result p4
new-instance v3, Lorg/jfedor/frozenbubble/LevelManager;
move-object v0, v3
move-object/from16 v1, v22
move/from16 v2, p4
"p4" 用以保存 method invocation 之後的回傳值,顯然,Register v2 受到 p4 的指派,也就是被更動為 android.content.Shared.Preference.getInt() method 的回傳值,這存在不確定性,於是,我們乾脆一口氣改掉: (修改的部份會用井字號 "#" 作註解)
# Modified from 0x0 to 0x4"
const/4 v4, 0x4
move-object/from16 v0, v25
move-object v1, v3
move v2, v4
# Modified: removed the following 2 lines
# invoke-interface {v0, v1, v2}, Landroid/content/SharedPreferences;->getInt(Ljava/lang/String;I)I
# move-result p4
new-instance v3, Lorg/jfedor/frozenbubble/LevelManager;
move-object v0, v3
move-object/from16 v1, v22
# Modified: removed the following 1 line
# move/from16 v2, p4
invoke-direct {v0, v1, v2}, Lorg/jfedor/frozenbubble/LevelManager;-><init>([BI)V
改好程式,當然要驗證,回到上一層目錄,透過 smali 提供的組譯器,重新產生 Dalvik DEX 輸出,為了簡化流程,筆者把 smali, apkbuilder, aapt, adb install 都一次整合進去,所以會直接讓 Android Emulator 生效,來看看我們的戰果吧:



 
注意到左下角,這表示我們成功了,完全不用取得 Java 原始程式碼,就可以作反組譯並且修改的動作。
  • 大小: 135.7 KB
  • 大小: 69.6 KB
  • 大小: 141.6 KB
分享到:
评论

相关推荐

    Androidapk反编译之androidAPK有效加密方法.pdf

    通过对 Android APK 的反编译和安全检测,我们可以看到混淆代码和防二次打包已经无用,而 DEX、RES、SO 库等文件的加密是有效的,因为我们根本无法拿到里面的代码,无法进行任何修改。这是我的 Android APK 防止破解...

    android apk反编译工具集合

    本文将详细介绍四个常用的Android反编译工具:apktool、dex2jar、jdgui以及Smali2Java,它们在Android APK文件处理中的作用。 首先,我们来看`apktool`。Apktool是一款开源的Android APK反编译工具,由IzzySoft开发...

    Android APK反编译工具反编译工具包

    这个“Android APK反编译工具反编译工具包”就是为了这些目的而准备的。它包含了三个核心工具:apktool、dex2jar和jd-gui,这些都是Android反编译流程中的关键组件。 首先,`apktool`是一个开源工具,用于解包和...

    Android APK反编译三件套,ApkTool、dex2jar、ju-gui,完美可用

    然而,由于Android应用的APK文件本质上是可被解压和分析的ZIP格式,因此APK的反编译成为了开发者和安全研究人员关注的领域。本文将详细介绍Android APK反编译的三个核心工具:ApkTool、dex2jar以及JD-GUI,并探讨...

    apk反编译工具合集

    为了对APK进行分析、修改或者二次开发,开发者有时需要反编译APK。"apk反编译工具合集"就是这样一个工具集合,它包括了apktool、dex2jar和jd-gui.exe这三个关键工具,它们各自在反编译流程中扮演着不同的角色。 ...

    android apk反编译工具

    有时候,开发者或者安全研究人员需要深入探究APK的内部结构,以理解其工作原理、修改资源或进行安全分析,这时就需要用到反编译工具。"Android APK反编译工具"正是为这个目的而存在的,其中最常用的一款工具就是`...

    Android apk反编译工具集

    Androidfby(又名ApkRepack)是一款用于APK打包、反编译和重新打包的工具,它可以帮助开发者快速解包、修改和重新签名APK文件。这个工具可以方便地处理APK的资源文件和.dex字节码。 2. **apktool**: Apktool是由...

    命令行修改Android apk包xml二进制文件

    在Android应用开发中,...总结,命令行修改Android APK包中的XML二进制文件涉及APK的结构理解、解压与打包操作、XML反编译与编译,以及签名和优化流程。通过这些步骤,开发者可以实现对APK的定制化修改,满足特定需求。

    Android Apk一键反编译 (支持Mac 和 Windows)

    "Android Apk一键反编译 (支持Mac 和 Windows)"是一个专为这一需求设计的工具,它简化了在不同操作系统上的反编译过程。 该工具的核心功能是提供了一键式的反编译体验,意味着用户无需进行复杂的命令行操作或手动...

    反编译google android apk文件的方法

    反编译Google Android APK文件的方法 Android APK 文件是 Android 操作系统中的一种应用程序安装包格式,它包含了应用程序的代码、资源和配置文件等。反编译 APK 文件可以获取应用程序的源代码、图片、XML 配置、...

    安卓android apk 反编译步骤

    安卓Android APK反编译是开发者或逆向工程师为了查看、分析或修改APK内部代码和资源的一种技术。以下是一个详细的步骤,帮助你理解如何进行这个过程: 1. **准备工作**:首先,你需要下载两个关键工具,分别是`dex2...

    Android apk 反编译

    Android APK反编译是一个复杂而重要的过程,它涉及到对Android应用程序的源代码进行逆向工程,以便理解其内部工作原理、提取资源或者进行修改。在Android应用开发中,APK文件是应用程序的打包形式,包含了所有的代码...

    androidapk反编译or打包工具

    androidapk反编译or打包工具,反编译apk包进行修改并重新打包

    apk反编译及签名打包工具组

    为了理解APK的工作原理、修改或优化其中的组件,开发者有时需要进行反编译操作。本工具组就是专为这类需求设计的,包含了一系列工具,能够帮助我们对APK进行全方位的修改和打包。 首先,我们要了解的是`apktool`。...

    Android apk反编译工具整理

    本文将详细介绍Android APK反编译工具及其重要性,以及如何使用集成aapt的res反编译、dex的反编译和class的反编译等技术来揭示APK的真实结构。 首先,让我们了解什么是反编译。反编译是将已编译的二进制代码转换回...

    android反编译工具

    8. **Androguard**:Androguard是一个开源的Android安全分析框架,可以用来分析APK文件,包括反编译DEX文件,提取类、方法和权限信息,还可以进行动态分析。 9. **Frida**:虽然主要是一个动态代码插桩工具,Frida...

    Android反编译软件 安卓反编译 Android APK反编译详解

    在Android开发领域,有时我们需要对APK文件进行反编译以了解其内部结构、源代码逻辑或进行二次开发。本文将深入探讨Android反编译的相关知识,包括为什么要进行反编译,常用工具介绍,以及反编译过程的详细步骤。 ...

    Android反编译APK查看

    在Android开发领域,有时我们需要对已有的APK应用进行反编译来查看其源代码、资源文件或了解其工作原理。这通常是出于学习、调试、安全分析或逆向工程的目的。本文将详细介绍如何使用名为JD-GUI的工具来实现Android ...

    最新mac版本android反编译工具包

    在Android应用开发领域,有时我们需要对APK文件进行反编译以查看其源代码、资源文件或进行二次开发。这个“最新mac版本android反编译工具包”正是为这一目的而设计的,它包含了适用于MAC OS 10及更高版本的必备工具...

Global site tag (gtag.js) - Google Analytics