前言
為什么要反編譯別人的代碼
- 人家比咱寫的好,學習?
- 了解別人某個功能實現,參考?
- 看中了人家的本地數據庫或者其他資源想要,但是人家的本地數據庫加密了,要讀源碼才能解密?
- 分析競品使用了哪些廣告,或者什么策略?
- 破解vip限制?
······
jadx的高級技巧
介紹
jadx是史上最好用的反編譯軟件,有以下優點:
- 圖形化的界面
- 拖拽式的操作
- 反編譯輸出 Java 代碼
- 導出 Gradle 工程
- 支持.dex, .apk, .jar or .class
- 反混淆
- 支持代碼跳轉
- 支持搜索文本,類
這些優點都讓 jadx 成為我反編譯的第一選擇,它可以處理大部分反編譯的需求,基本上是我反編譯工具的首選。
安裝 官網
jadx 本身就是一個開源項目,源代碼已經在 Github 上開源了。有興趣可以直接 clone 源代碼,然后本地自己編譯。但是多數情況下,我們是需要一個編譯好的版本。編譯好的版本,在github是也可以直接下載到,下載最新版本,現在的最新版是 jadx-0.8.0 。下載好解壓之后,你會獲得這樣的目錄結構:
對于 Mac 或者 Linux,使用 jadx-gui ,Windows 下就需要使用 jadx-gui.bat 了,雙擊可以直接運行,如果有安全警告,忽略它就可以了。(后文主要以 Windows 環境為講解, 其他平臺下的大部分操作都是類似的)
使用
直接雙擊前面解壓出來的jadx-gui.bat就可以直接運行。運行之后會打開一個界面,有一個文件選擇彈窗,你可以選擇一個 apk、dex、jar、zip、class、aar 文件,可以看到 jadx 支持的格式還是挺多的,基本上編譯成 Java 虛擬機能識別的字節碼,它都可以進行反編譯。除了選擇一個文件,還可以直接將 apk 文件,拖拽進去,這一點非常好用。下面給大家感受一下jadx的強大:
-
搜索功能
jadx 提供的搜索功能,非常強大,而且搜索速度也不慢。點擊 Navigation -> Text Search 或者 Navigation -> Class Search 激活它,并且 jadx 的搜索支持四種維度,Class、Method、Field、Code,我們可以根據我們搜索的內容進行勾選,范圍最大的就是 Code ,基本上就是文本匹配搜索。 -
查找引用
比如想要找到我們想要的類和代碼,那么可以直接使用 jadx 的搜索代碼功能,找到我們需要查看的類或者代碼,選中點擊右鍵,選擇Find Usage -
反混淆(Deobfuscation)
一般 apk 在發布出去之前,都是會被混淆的,這基本上是國內 App 的標配,但其實非常不利于我們閱讀。我們很難看到一個 a.java 的文件之后,就確定它是哪一個,還需要根據包名來區分。而 Deobfusation 功能,可以為它們起一個特殊的名字,這樣它在這個項目中,名字就是唯一的,方便我們識別和搜索。這個功能可以在 Tools -> deobfusation 中激活 -
導出Gradle工程
jadx-gui 可以直接閱讀代碼,還是很方便的。但是畢竟沒有我們常見的編輯器來的方便。jadx支持將反編譯后的項目,直接導出成一個 Gradle 編譯的工程。可以通過 File -> Save as gradle project 來激活這個功能。最終輸出的項目,可以直接通過 Android Studio 打開。
可能出現的問題
- 資源文件可能有缺失,資源文件還是通過apktool來獲取
- inconsistent code
有時候有代碼,反編譯的不完整,你會看到 JADX WARNING : inconsistent code 標志的錯誤。jadx 為了應對這樣的情況,可以嘗試開啟 Show inconsistent code 開關。你可以在 File -> Preferences 中找到它。這樣處理的代碼,大部分為偽代碼,可能會有錯誤的地方。 - 在反編譯較大的apk時,如果遇到jadx-jui卡頓和假死的情況,可適當優化jvm相關參數
- 減少處理的線程數
jadx 為了加快編譯的效率,所以是使用多線程處理的,而多個線程會耗費跟多的內存。所以減小反編譯時候的線程數,是一個有效的方法。如果使用命令行的話,可以使用 -j 1 參數,配置線程數為 1,不配置的話,默認線程數為 4。而使用 jadx-gui 的話,可以在 Preferences 中,通過配置 Processing threads count 來配置線程數 - 修改 jadx 腳本
直接編輯 ./bin 目錄下的 jadx.bat腳本,配置找到 DEFAULT_JVM_OPTS ,將它設置為 DEFAULT_JVM_OPTS="-Xmx2500M" ,就可以配置當前使用的內存大小。 - 使用命令行命令
如果以上方式都不好用,在沒有更好的辦法的情況下,你可以直接使用命令行,通過 jadx 的命令進行放編譯。并將線程數配置為 1 ,這樣雖然慢一些,但是多數情況下,是可以正常輸出反編譯后的代碼的。
舉個例子:
jadx -d out -j 1 classes.dex
更過命令,可以通過 jadx -h 命令進行查看。
仔細看看 jadx 命令配置的參數,基本上都可以在 Preferences 中,找到對應的配置項,相互對照理解一下,應該不難發現它的使用方式。
- 減少處理的線程數
apktool查看資源文件
利用apktool查看apk的xml文件、AndroidManifest.xml和圖片等,也可以查看src目錄下的smali文件。
安裝 官網
- 下載apktool.bat,把鼠標移至wrapper script上,然后右擊,鏈接另存為…,把下載來的文件放到如E:\Android\apktools,記得名字要改成apktool.bat;
- 下載apktool.jar文件,點擊find newest here,跳到下載頁,我們能盡量下載最新版本,舊版本可能不能用,我這里下載最新版本apktool_2.3.4.jar,也把該文件剛到apktool文件夾下。
- 把你下載來的jar文件重新命名為:apktool.jar。
- 官網建議你把apktool.bat、apktool.jar放到C盤的Windows下,也可以不用,但是需要把存放的路徑配置到環境變量的PATH中
使用
-
win+R,輸入cmd調出命令行窗口,切換到apktool文件夾目錄下。接下來把apk拷貝到E:\Android\apktools下面,然后在cmd窗口輸入命令,回車,如下圖:
apktool d yourapp.apk
apktools_d.png
這樣就表示成功了,我們就可以在E:\Android\apktools發現一個新的文件夾yourapp(這個文件夾的文字跟你的apk名字一樣),里面我們就可以看到xml文件、AndroidManifest.xml和圖片等資源文件了。
經過上面的步驟,我們可以在文件夾yourapp中發現一個文件夾smali,這里面其實就java代碼,只不過不是jar形式的,關于如何查看java源碼,可以通過dex2jar工具,這里不是我們關注的重點,不在這里贅述。 反編譯后想驗證自己的某些想法,或者代碼有些地方沒有看明白想添加log,可以通過修改圖片等資源文件或者smali源碼后再重新打包的方式。命令如下:
apktool b test
重新打包后新的未簽名apk生成的路徑在yourapp/dist/yourapp.apk。
修改圖片,或者strings.xml等資源文件,可以直接執行重新打包命令即可。如果需要修改smali源碼,要對smali語法有一定的了解,再進行修改。對打包后的apk簽名,未簽名的apk無法安裝到Android手機里。使用你自己的簽名文件進行簽名,簽名命令參考如下:
apksigner.bat sign --ks yourapp\keystore.jks --ks-key-alias keystore yourapp\dist\yourapp.apk
也可以使用其他三方的apk簽名工具進行簽名。優化apk包,這一步是可選的。用來將apk包進行整理,以適應設備的讀取等
zipalign.exe -f -v 4 yourapp.apk yourapp_zip.apk
-f 強制覆蓋已有的文件
-v 輸出詳細內容
4 指定檔案整理的字節數,一般為4,即32位
yourapp.apk 是未整理的apk文件名 yourapp_zip.apk 是整理后的apk文件名
檢查apk有沒有zipalign對齊:
zipalign -c -v 4 被檢查的apk文件
對齊安裝時會可能會遇到錯誤
先簽名再對齊,否則先對齊再簽名會破壞對齊正常安裝簽名后的apk即可體驗。
android studio無源碼動態調試apk
主要過程就是AndroidStudio動態調試Smali,是非常有效的逆向分析方法。把apk反編譯成Smali然后倒入AndroidStudio中,然后通過jdwp調試相關進程。
基本技能
工具
- Android Studio最新版本
- smaliidea-x.x.x.zip這個是AndroidStudio的插件,從這個鏈接的列表中下載那個,最新版本的zip文件插件的官網
- apktool 反編譯apk->Smali 并且重新打包修改后的Smali到apk
- jadx 用了查看Smali對應的java代碼,增加可讀性
動態調試Smali文件
-
調試的前提條件 使app可調試
要想調試一個apk的前提是這個apk是可調式,一般我們發版的時候,會發release版。以前,我們開發Android是沒有gradle的,那時候發release版不像現在在gradle配置好就行了,是直接操作AndroidManifest.xml文件中標簽的屬性 android:debuggable="true",因為在一般的手機上,release版本的應用是不可以被調試的,相對來說起到了保護app的作用。
從上面來看,可以在AndroidManifest文件中設置debuggable開關,那么這個開關是被誰來驗證的呢?答案是系統,Android系統會通過debuggable 驗證一個app是不是可以調試。可以不可以關掉系統的驗證?答案是可以的。不過很麻煩,據說有兩種方式可以修改,一種是重新刷入boot.img 修改方法,另一種是通過xpost修改。
而我們平常用的最多的就是,修改AndroidManifest.xml 中的android:debuggable="true",然后重新打包apk。
舉例前段時間看的一個鬧鐘應用alarmy.apk:- 通過apktool d alarmy.apk來反編譯
- 在生成的目錄中找到AndroidManifest.xml,用AS或者文本編輯器打開修改里面的標簽,如果有debuggable屬性,修改為true,如果沒有,給標簽添加android:debuggable="true"
- apktool b alarmy這時候會在./alarmy/dist目錄下生成重新打包好的apk。
- 然后要給這個apk簽名,參考簽名說的簽名部分。
- 然后我們把這個自簽名后的apk安裝到手機就可以了
-
導入Smail源碼到AndroidStudio中
打開as后,通過File-->Open ...選擇我們剛才反編譯處理的那個目錄,alarmy,然后等待as建立完索引。
注意左側選擇Project視圖,如下所示:
android_studio_project.png
然后右鍵工程主目錄:Mark Directory as -> Sources Root
然后設置sdk,最好和測試手機的系統版本一致:項目目錄-->右鍵-->Open Module Settings:
-
Android Studio 的配置
接下來配置:Run/Debug Configurations里面的配置文件:
edit_confi.png
打開后我們點擊上面的+符合,然后選擇Remote,添加一個遠程調試如下圖:
然后配置遠程調試的端口和一些其他信息,如下圖:
注意,上面的Name可以隨便寫,因為每一個Remote配置都對應手機app上的一個進程,每一個手機app可能有多個進程,所有名字上我們做下區分。另一個需要配置的地方是Port,這個port也可以隨便寫,只要當前電腦上沒有是用這個端口就好,如果要同時調試手機上的某個app的多個進程,這個每次配置Remote的時候,port不能一樣。我們這里是用默認的5005。
-
打通AndroidStudio和可調試apk之間的通道
手機上已經安裝了我們前面重新打包的可調試的alarmy的apk,啟動它-
查看alarmy的所有的進程信息
先找到當前的包名,可以通過查看當前的activity來獲取:
adb shell dumpsys activity activities
activity.png
然后命令行運行:
adb shell ps | findstr droom.sleepIfUCan
ps.png 判斷你要debug的那個頁面(Activity)在哪個進程里面
首先打開這個頁面,然后命令行運行:
adb shell dumpsys activity | findstr mFocusedActivity
或者
···adb shell dumpsys activity | findstr mResumedActivity```
這會得到當前顯示的Activity的名字,然后去AndroidManifest.xml中去查看這個Activity的信息,里面會有進程信息。端口映射
adb forward tcp:5005 jdwp:5972
設置端口轉發,這條命令的含義可以認為是在本地5005端口與手機5972進程之間建立一條通道,當開始調試時,AS連接本地的5005端口,通過這條通道控制程序的運行。這個5005是前面(圖3.2)中配置的端口,這個5972是alarmy在手機上運行的一個進程的進程id,在前面執行ps時獲取的。-
加斷點
和平常一樣,只要加到你想要程序暫停的地方就好,我們把斷點下到alarmy的首頁,通過adb shell dumpsys activity | findstr mResumedActivity這個命令可知道首頁叫droom.sleepIfUCan/.view.activity.MainActivity。
onresume.png
注:這里把斷點下到了首頁的onResume方法中是為了測試用,因為onResume方法會被調用很多次,當我們按home鍵,然后在打開alarmy的時候這個方法就會被調用。
-
啟動debug
debug.png
首先選擇要調試的配置,然后點擊那個調試按鈕。如果左下角出現下圖說明啟動成功:
connected.png
試試打開個別的應用,然后再切回alarmy,這時候程序會停在斷點處。
pause.png -
鏈接失敗處理
使用過程中很容易出現如下錯誤:
error.png
嘗試了修改端口號,重啟等一系列方法都沒有效果。 最后找到了一種簡單有效的辦法,直接用attach的方式找到進程再attach上去,但是沒有找到打開Choose Process窗口的入口,只能通過快捷鍵的方式來打開:
先綁定快捷鍵
keymap.png
這里我設置了ctrl+alt+R,也可以設置其他不沖突的。然后再直接用快捷鍵打開:
attach.png
找到需要的進程即可
-
代碼注入
先通過jadx閱讀目標app的代碼,方便閱讀,降低難度,然后再通過Android Studio來編輯smali代碼。簡單的可以直接修改某個值,復雜的可以先寫好代碼,然后將該段反編譯出smali代碼,再復制黏貼到需要注入的目標smali代碼。這里需要注意smali的一些語法。
這里舉個簡單的例子,想獲知alarmy監聽了哪些廣播,添加Log打印。
-
先寫好要添加的java代碼:
log_java.png -
通過jadx查看需要添加的代碼:
log_jadx.png -
通過apktool反編譯準備注入的代碼:
log_smali.png - 把上述smali代碼復制黏貼到目標smali的方法處,如下:
inject_source.png
修改Smali時有一件很重要的事情就是要注意寄存器。如果亂用寄存器的話可能會導致程序崩潰。每個方法開頭聲明了registers的數量,這個數量是參數和本地變量總和。參數統一用P表示。如果是非靜態方法p0代表this,p1-pN代表各個參數。如果是靜態方法的話,p0-pN代表各個參數。本地變量統一用v表示。如果想要增加的新的本地變量,需要在方法開頭的registers數量上增加相應的數值。