Android 逆向分析大全
?
轉載:Android 逆向分析大全:https://www.jianshu.com/p/a12d04fc748f
?
?
?
1. 概述
?
1.1 分析步驟
?
通用逆向分析步驟
- 1. 了解該模塊正向編程相關方法
- 2. 使用apktool解密apk,得到資源、jni模塊等文件
- 3. 從apk提取出dex文件,使用dex2jar轉換成jar文件,再用java逆向工具得到java源碼 dex->jar->java
- 4. 根據特征(字符串、常量、包名類名方法名、manifest文件、布局文件等方式)或調試手段定位到關鍵代碼
- 5. 分析變量含義類型、函數邏輯、模塊流程
- 6. 對變量、函數、類進行標注、恢復成高級語言 ->c
Android程序的特點相比在于使用混淆方式打包,將包名、類名、函數名改成不易看懂的字母,從而使生成的apk小很多(android studio提供了release編譯方式,使用proguard混淆),因此反編譯apk最多的工作在于重構這些名稱,這一點和pc上一致,對于android native程序(jni)則和pc上基本一致,不同之處在于常見的是arm匯編。
?
安卓上APK調試步驟:
- 1.Apk(debuggable)或系統(ro.debuggable=1)設置為可調試
- 2.在虛擬機中啟動服務端(adbd/android_server)
- 3.在主機端連接客戶端調試器(IDA/jdb/adt),設置斷點
?
安卓上linux程序調試步驟:
- 1.在虛擬機中啟動服務端(gdb_server/linux_server)
- 2.在主機端連接客戶端調試器(IDA/gdb_for_windows),設置斷點
對于apk的反編譯,由于資源和xml都進行了編碼,因此反編譯時必然要解析相應的resource.arsc/AndroidManifest.xml等文件,對于做過保護處理的apk通常會在這里做手腳干擾Apktool、dex2jar等反編譯工具因此很有必要掌握編譯、調試這些工具源碼的方法(見“如何編譯、調試apktool和dex2jar”)
?
?
1.2 分析工具
?
- 集成IDE:APK改之理、JD-GUI、JEB(1.4破解 2.0)、jadx
- 解壓(apk, jar):WinRar
- 解析資源:apktool
- 反編譯引擎(jar, class):dex2jar工具集、jd-core(JD-GUI,JD-Eclipse反編譯核心)、fernflower(Android Studio反編 、procyon
- 回編譯:aapt、dex2jar工具集
- 調試器:IDA、jdb、adt等
- 輔助工具:DDMS 如果是虛擬機可以看到所有進程
?
APK 改之理
- 整合&提供了全套解壓、反編譯代碼和資源、回編譯、簽名功能,強大的正則搜索,修改smali字節碼等功能
- 集成ApkTool、Dex2jar、JD-GUI工具
- 可視化操作,全自動的反編譯、回編譯、簽名Apk
- 正則表達式搜索資源及源碼
?
JD-GUI
輕量級反編譯,反編譯jar/class等java字節碼文件(能力一般),提供簡單的搜索能力
?
JEB
- 反編譯apk/jar工具(能力較強)
- 強大的正向、反向索引,一定程度重命名能力,一定搜索能力
- 支持注釋、插件
- 交互式可視化操作,全自動的反編譯
- 支持重命名
?
Dex2jar 工具集
dex2jar是一個工具包,反編譯dex和jar,還提供了一些其它的功能,每個功能使用一個bat批處理或 sh 腳本來包裝,只需在Windows 系統中調用 bat文件、在Linux 系統中調用 sh 腳本即可。在bat中調用相應的jar主類完成特定功能,例如d2j-dex2jar.bat中的內容是:@"%~dp0d2j_invoke.bat" com.googlecode.dex2jar.tools.Dex2jarCmd %*。常用的有dex2jar jar2dex dex2smali smali2dex
- d2j-apk-sign用來為apk 文件簽名。命令格式:d2j-apk-sign xxx.apk 。
- d2j-asm-verify 用來驗證jar 文件。命令格式:d2j-asm-verify -d xxx.jar。
- d2j-dex2jar 用來將dex 文件轉換成jar 文件。命令格式:d2j-dex2jar xxx.apk
- d2j-dex-asmifier 用來驗證dex 文件。命令格式:d2j-dex-asmifier xxx.dex。
- d2j-dex-dump 用來轉存dex 文件的信息。命令格式:d2j-dex-dump xxx.apk out.jar 。
- d2j-init-deobf 用來生成反混淆jar 文件時的初始化配置文件。
- d2j-jar2dex 用來將jar 文件轉換成 dex 文件。命令格式:d2j-jar2dex xxx.apk。
- d2j-jar2jasmin 用來將jar 文件轉換成jasmin 格式的文件。命令格式:d2j-jar2jasmin xxx.jar
- d2j-jar-access 用來修改jar 文件中的類、方法以及字段的訪問權限。
- d2j-jar-remap 用來重命名jar 文件中的包、類、方法以及字段的名稱。
- d2j-jasmin2jar 用來將jasmin 格式的文件轉換成 jar 文件。命令格式:d2j-jasmin2jar dir dex2jar為d2j-dex2jar 的副本。
- dex-dump為d2j-dex-dump 的副本
?
Apktool反編譯&打包工具
- 反編譯apk:apktool d file.apk –o path
- 回編譯apk:apktool b path –o file.apk
?
?
1.3 常見文件格式
?
Apk
Android package,android安裝程序文件,本質上是壓縮包,解壓得到classes.dex、resources.arsc、AndroidManifest.xml、so文件以及資源文件
- Resources.arsc資源描述文件
- Classes.dex所有代碼編譯過得darvik字節碼文件,可能會有多個
- AndroidManifest.xml 編譯過的AndroidManifest.xml文件
使用 aapt 解析 xml
aapt d xmltree 1.apk AndroidManifest.xml N: android=http://schemas.android.com/apk/res/androidE: manifest (line=2)A: android:versionCode(0x0101021b)=(type 0x10)0x1A: android:versionName(0x0101021c)="1.0" (Raw: "1.0")A: package="com.ibotpeaches.issue767" (Raw: "com.ibotpeaches.issue767")A: platformBuildVersionCode=(type 0x10)0x17 (Raw: "23")A: platformBuildVersionName="6.0-2438415" (Raw: "6.0-2438415")E: uses-sdk (line=0)A: android:minSdkVersion(0x0101020c)=(type 0x10)0x16A: android:targetSdkVersion(0x01010270)=(type 0x10)0x17E: application (line=3)A: android:theme(0x01010000)=@0x7f090083A: android:label(0x01010001)=@0x7f060015A: android:icon(0x01010002)=@0x7f030000A: android:debuggable(0x0101000f)=(type 0x12)0xffffffffA: android:allowBackup(0x01010280)=(type 0x12)0xffffffffA: android:supportsRtl(0x010103af)=(type 0x12)0xffffffffE: activity (line=4)A: android:theme(0x01010000)=@0x7f090030A: android:label(0x01010001)=@0x7f060015A: android:name(0x01010003)="com.ibotpeaches.issue767.MainActivity" (Raw : "com.ibotpeaches.issue767.MainActivity")E: intent-filter (line=5)E: action (line=6)A: android:name(0x01010003)="android.intent.action.MAIN" (Raw: "andr oid.intent.action.MAIN")E: category (line=7)A: android:name(0x01010003)="android.intent.category.LAUNCHER" (Raw:"android.intent.category.LAUNCHER")E: meta-data (line=10)A: android:name(0x01010003)="large.int.value" (Raw: "large.int.value")A: android:value(0x01010024)="9999999999999999999999" (Raw: "99999999999 99999999999")查看xml => aapt d xmltree 1.apk AndroidManifest.xml
查看resource => aapt d resources 1.apk (resource.arsc)
?
Dex
Dalvik Executable,Dalvik可執行文件,從java class文件轉換而來的字節碼,Classes.Dex通過dex2jar轉換成java字節碼(有損),或者dex2smali轉換成darvik匯編(無損)——smali字節碼,其形式如下
?
Jar
Java Archive,java歸檔文件,可以直接解壓得到class文件
?
Odex
dex 轉 odex:/system/bin/dexopt
dexopt-wrapper 1.apk 1.odex
?
Aar
Android 歸檔文件,壓縮包格式,包含
-
/AndroidManifest.xml (強制) 未編譯的
-
/classes.jar (強制)
-
/res/ (強制)
-
/R.txt (強制)
-
/assets/ (可選)
-
/libs/*.jar (可選)
-
/jni/
<abi>/*.so (可選)</abi>
-
/proguard.txt (可選)
-
/lint.jar (可選)
?
So
Linux 動態鏈接庫文件,包含arm64 arm mips mips64 x86 x86-64幾個平臺
?
工具轉換圖
?
Android 設備上重要目錄
- /system/app/1.apk 系統應用
- /data/app/1.apk 用戶應用
- /data/data/[pkgname] 應用數據(so,database,…)
- /data/dalvik-cache 存放dex
?
2. Java 層?
?
2.1 常用工具
?
adb
設備通信、調試工具,常用法:
adb devices 列出當前設備 adb –s d24eb3ab [命令] 指定設備執行命令 adb push 源 目標 非root機器可以設置路徑為/data/local/tmp adb pull 源 目標 adb shell 執行終端 adb logcat 查看日志(/system/logcat為服務器) adb jdwp 查看遠程jdwp進程 adb forward tcp:主機端口 tcp:遠程端口 把主機端口消息轉發手機端口(端口對應進程) 用于ida調試 adb forward tcp:主機端口 jdwp:遠程進程ID 把主機端口消息轉發手機jdwp進程 用于jdb調試 adb install [apkpath] 安裝apk adb uninstall [packagename] 卸載apk 注意會徹底清理,刪除/data/app下的備份apk adb remount 將/system重新映射為讀寫,以便進行系統區文件操作 adb root 使adb以root方式啟動,便于push/pull/remount?
aapt
APK資源管理工具,用于增刪查改APK中的文件、資源等,對于分析編譯后的Resource.arsc, AndroidManifest.xml格式較有價值,通常也可以用winrar對apk/jar進行解壓
打印xml樹 aapt d xmltree 1.apk AndroidManifest.xml 打印資源 aapt d resources 1.apk 添加文件 aapt a 1.apk AndroidManifest.xml 刪除文件 aapt r 1.apk AndroidManifest.xml?
am & pm
Android 遠程命令,am 執行調試、運行功能,pm 執行安裝、卸載功能
- 啟動應用:am start -D -n "b.myapp/b.myapp.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
- 啟動服務:am startservice -n com.android.music/com.android.music.MediaPlaybackService
- 強制停止包:am force-stop com.example.administrator.myapplication
- 強制結束包進程:am kill com.example.administrator.myapplication am kill all
- 發送廣播:adb shell am broadcast -a com.android.test
- 安裝應用:pm install –r 1.apk
- 卸載應用:pm uninstall packagename
- 列出所有安裝包:pm list package
- 查看是否以指定名為前綴的包存在:pm list package com.qihoo
- 禁用應用:pm disable packagename (禁用后,圖標消失,對該應用的操作都無效)
?
?
2.2 有源碼調試 APK
?
Android studio
在 android studio 中可以采用運行調試或進程附加方式調試,支持條件斷點、一次斷點、對單線程下斷,有6種斷點:
| 行斷點 | Java Line Breakpoints | 在(java/c)源碼某行下斷 |
| Java類成員變量訪問斷點 | Java Field Watchpoints | 類似于內存訪問斷點,在讀和寫java類成員變量時斷下 |
| Java類方法斷點 | Java Method Breakpoints | 在進入java層函數或退出函數時斷下 |
| Java異常斷點 | Java Exception Breakpoints | 發生java層捕獲或未捕獲異常時斷下 |
| 異常斷點 | Exception Breakpoints | 拋異常或捕獲異常時斷下 |
| 符號斷點 | Symbolic Breakpoints | (c/java)符號斷點 |
?
Adb wifi
應用市場有很多這種軟件,需要Root權限。解決沒有USB數據線的情況下的調試
C:\Users\Administrator>adb connect 192.168.0.103:5555 connected to 192.168.0.103:5555 此時可以用adt調試?
?
2.3 無源碼調試apk
?
不需要調試的一般過程 :使用反編譯工具得到源代碼,修改調試標識,修改機器碼,最后回編譯簽名:
反編譯apk:apktool d file.apk –o path 回編譯apk:apktool b path –o file.apk?
使用 AndroidStudio 和 Apktool 工具調試
- 第一步,反編譯得到(占行)偽源碼:java -jar apktool.jar d -d input.apk -o out,加上-d選項之后反編譯出的文件后綴為.java,而不是.smali,每個.java文件立馬都偽造成了一個類,語句全都是“a=0;”這一句,smali語句成為注釋,做這些都是為了后面欺騙idea、eclipse、android studio這些ide的
- 第二步,修改資源或者源碼(smali),修改AndroidManifest.xml調試標識,反編譯以后可以在dex中插入waitfordebugger或者Log.i的smali代碼來進行相應的控制
- 第三步,回編譯(-d選項) + 簽名? ? ? ??
?? ??? ?回編譯:apktool b –d path –o input.apk
? ? ? ? 簽名: java –jar signapk.jar testkey.x509.pem testkey.pk8 input.apk output.apk - 第四步,新建android studio工程 ,將反編譯得到的smali文件夾中的源文件拷貝到源碼目錄(欺騙),回編譯的apk覆蓋目標apk位置 ,刪除Edit configuration的Before launch,下斷點調試
點評:這種方式只可以用來分析加密很弱的App,前提是apktool可以成功反編譯
?
?
使用 jdb 調試
jdb是一個支持java代碼級調試的工具,它是由java jdk提供的,可以設置斷點、查看堆棧、計算表達式、動態修改類字節碼、調試&跟蹤、修改變量值、線程操作,斷點包括:(源碼)行斷點、符號斷點、成員變量訪問斷點。每個java程序(windows/ios/android)都可以用jdwp協議進行調試,Android Studio/Eclipse的調試也是建立在該協議基礎之上,下面以實例說明:
-
第一步,開發 demo
public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);findViewById(R.id.ok).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.b.com"));intent.setClassName("com.android.browser","com.android.browser.BrowserActivity");startActivity(intent);}});} } -
第二步,啟動 jdb 調試
adb shell am start -D -n "b.myapp/.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER?
-
第三步,開始調試
行斷點 stop at android.app.Activity:123,觸發斷點后顯示堆棧:``` <1> main[1] where [1] android.app.Activity.startActivity (Activity.java:3,490) [2] b.myapp.MainActivity$1.onClick (MainActivity.java:21) [3] android.view.View.performClick (View.java:4,084) [4] android.view.View$PerformClick.run (View.java:16,966) [5] android.os.Handler.handleCallback (Handler.java:615) [6] android.os.Handler.dispatchMessage (Handler.java:92) [7] android.os.Looper.loop (Looper.java:137) [8] android.app.ActivityThread.main (ActivityThread.java:4,745) [9] java.lang.reflect.Method.invokeNative (本機方法) ```
? ? ? ? 查看 ddms 中該進程端口號 8600
? ? ? ? 使用 jdb 調試:jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8600
? ? ? ? 下斷點:函數斷點stop in android.app.Activity.startActivity(android.content.Intent)
?
查看參數
<1> main[1] print intentintent = "Intent { act=android.intent.action.VIEW dat=http://www.b.com cmp= com.android.browser/.BrowserActivity }"設置源碼從而進行逐行調試
<1> main[1] use D:\Android\sdk\sources\android-18 //參考設備android版本 <1> main[1] use D:\test\MyApplication\app\src\main\java <1> main[1] list 3,421 * @hide Implement to provide correct calling token. 3,422 */ 3,423 public void startActivityAsUser(Intent intent, UserHandle user) { 3,424 startActivityAsUser(intent, null, user); 3,425 => } 3,426 3,427 /** 3,428 * @hide Implement to provide correct calling token. 3,429 */ 3,430 public void startActivityAsUser(Intent intent, Bundle options, User Handle user) {行斷點:
> use D:\test\MyApplication\app\src\main\java stop at b.myapp.MainActivity:18 正在延遲斷點b.myapp.MainActivity:18。 將在加載類后設置。 > resume 已恢復所有線程。 > 設置延遲的斷點b.myapp.MainActivity:18 斷點命中: "線程=<1> main", b.myapp.MainActivity.onCreate(), 行=18 bci=12 18 int j = 0;初始斷點
只要連接到 jdb 就會導致 app 運行起來,此時如果想斷在初始化這部分就沒有辦法了,不過 jdb 提供初始命令腳本
- 暫停所有線程: echo suspend > jdb.ini
- 執行調試:jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8601
此時,app 仍然處于等調試器狀態,而蟲子已經變綠,此時可以下斷點,然后 resume 恢復所有線程
附加后會變綠色蟲子
> > stop in b.myapp.MainActivity.onCreate(android.os.Bundle) 正在延遲斷點b.myapp.MainActivity.onCreate(android.os.Bundle)。 將在加載類后設置。 >resume 已恢復所有線程。 斷點命中: "線程=<1> main", b.myapp.MainActivity.onCreate(), 行=13 bci=0<1> main[1] where[1] b.myapp.MainActivity.onCreate (MainActivity.java:13)[2] android.app.Activity.performCreate (Activity.java:5,372)[3] android.app.Instrumentation.callActivityOnCreate (Instrumentation.java:1,1 04)[4] android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2,25 8)[5] android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:2,350 )[6] android.app.ActivityThread.access$700 (ActivityThread.java:160)[7] android.app.ActivityThread$H.handleMessage (ActivityThread.java:1,317)[8] android.os.Handler.dispatchMessage (Handler.java:99)[9] android.os.Looper.loop (Looper.java:137)[10] android.app.ActivityThread.main (ActivityThread.java:5,454)調試命令
stop in:斷點step:步入(源碼行)stepi:單入(指令)step up:執行到返回cont:恢復運行next:步過輸出表達式:print/evaljdb 最大缺點在于難用,所以有人用 python 封裝了一次,工具名 AndBug
?
?
2.4 無源碼調試 dex
- 使用 ida 分析 apk 或者從 apk 中提取出的 dex
- 設置調試選項,包括包名和主類名,參考反編譯的 AndroidManifest
- 啟動調試即可
?
?
3. Linux 層?
?
3.1 常用工具
?
Gdbserver
Usage: gdbserver [OPTIONS] COMM PROG [ARGS ...]gdbserver [OPTIONS] --attach COMM PIDgdbserver [OPTIONS] --multi COMM隱藏用法:gdbserver [OPTIONS] +SOCKETFILE --attach PID 會在本地建立socket文件通信 Options:--debug Enable general debugging output.--remote-debug Enable remote protocol debugging output.--version Display version information and exit.--wrapper WRAPPER -- Run WRAPPER to start new programs.--once Exit after the first connection has closed. 使用方式: 啟動模式遠程調試:gdbserver --debug --remote-debug :23946 /system/test.out [參數] 附加模式遠程:gdbserver –debug –remote-debug –attach :23946 1234Adb forward tcp:23946 tcp:23946 轉發端口 IDA中選擇Remote GDB Debugger附加即可?
Strace
usage: strace [-CdDffhiqrtttTvVxx] [-a column] [-e expr] ... [-o file][-p pid] ... [-s strsize] [-u username] [-E var=val] ...[command [arg ...]] or: strace -c [-D] [-e expr] ... [-O overhead] [-S sortby] [-E var=val] ...[command [arg ...]] -c --統計每一系統調用的所執行的時間,次數和出錯的次數等. -C -- like -c but also print regular output while processes are running -f --跟蹤由fork調用所產生的子進程. -F --嘗試跟蹤vfork調用.在-f時,vfork不被跟蹤. -i --輸出系統調用的入口指針 -q --禁止輸出關于脫離的消息 -r --打印出相對時間關于,,每一個系統調用 -T --顯示每一調用所耗的時間 -v --輸出所有的系統調用.一些調用關于環境變量,狀態,輸入輸出等調用由于使用頻繁,默認不輸出 -x --以十六進制形式輸出非標準字符串 -a設置返回值的輸出位置.默認 為40. -e expr -指定一個表達式,用來控制如何跟蹤.: option=[!]all or option=[!]val1[,val2]...options: trace, abbrev, verbose, raw, signal, read, or write -o file --將strace的輸出寫入文件filename -O overhead -- set overhead for tracing syscalls to OVERHEAD usecs -p pid --跟蹤指定的進程pid. -D -- run tracer process as a detached grandchild, not as parent -s strsize --指定輸出的字符串的最大長度.默認為32.文件名一直全部輸出 -S sortby -- sort syscall counts by: time, calls, name, nothing (default time) -u username --以username 的UID和GID執行被跟蹤的命令 -E var=val -- put var=val in the environment for command -E var -- remove var from the environment for command使用方式: Strace –f ProcessA 啟動跟蹤 Strace –f –p 234 附加跟蹤-e trace=file -e trace=process -e trace=network?
?
3.2 有源碼 so 調試
?
Ndk-gdb
該程序是一個shell腳本,執行過程如下:
adb shell am start -D -n com.example.hellojni/.HelloJni 啟動app并等待調試器ps | grep hellojni 得到PID 3569 adb shell run-as com.example.hellojni /data/data/com.example.hellojni/lib/gdbserver +debug-socket --attach (3569)PID將PID與文件映射建立調試鏈接(c層) adb forward tcp:5039 localfilesystem:/data/data/com.example.hellojni/debug-socket將調試鏈接和本地端口建立鏈接(c層) adb forward tcp:65534 jdwp:(3569)PID 將本地端口和進程建立連接(java層) jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=65534 使用jdb調試java層 arm-linux-androideabi-gdb.exe target remote :5039 使用gdb調試c層set breakpoint pending on(使用前關掉騰訊的AndroidServer.exe,否則連不上!!!),在工程目錄下(有AndroidManifest.xml),命令行運行%NDK_ROOT%\ndk-gdb-py.cmd --start --verbose,輸出下面字符即為成功:
Android NDK installation path: D:/Android/AndroidNDK/android-ndk-r10e ADB version found: Android Debug Bridge version 1.0.32 Using ADB flags: Using auto-detected project path: . Found package name: com.example.hellojni ABIs targetted by application: arm64-v8a armeabi armeabi-v7a armeabi-v7a-hard mips mips64 x86 x86_64 Device API Level: 19 Device CPU ABIs: armeabi-v7a armeabi Compatible device ABI: armeabi-v7a Using gdb setup init: ./libs/armeabi-v7a/gdb.setup Using toolchain prefix: D:/Android/AndroidNDK/android-ndk-r10e/toolchains/arm-linux-androideabi-4.8/prebuilt/windows/bin/arm-linux-androideabi Using app out directory: ./obj/local/armeabi-v7a Found debuggable flag: true Found device gdbserver: /data/data/com.example.hellojni/lib/gdbserver Found data directory: '/data/data/com.example.hellojni' Found first launchable activity: .HelloJni Launching activity: com.example.hellojni/.HelloJni ## COMMAND: adb_cmd shell am start -D -n com.example.hellojni/.HelloJni ## COMMAND: adb_cmd shell sleep 2.000000 Found running PID: 9139 ## COMMAND: adb_cmd shell run-as com.example.hellojni /data/data/com.example.hellojni/lib/gdbserver --attach +debug-socket 9139 [BACKGROUND] Launched gdbserver succesfully. Setup network redirection ## COMMAND: adb_cmd forward tcp:5039 localfilesystem:/data/data/com.example.hellojni/debug-socket Attached; pid = 9139 Listening on Unix socket debug-socket ## COMMAND: adb_cmd pull /system/bin/app_process ./obj/local/armeabi-v7a/app_process 79 KB/s (9488 bytes in 0.117s) Pulled app_process from device/emulator. ## COMMAND: adb_cmd pull /system/bin/linker ./obj/local/armeabi-v7a/linker 585 KB/s (63596 bytes in 0.106s) Pulled linker from device/emulator. ## COMMAND: adb_cmd pull /system/lib/libc.so ./obj/local/armeabi-v7a/libc.so 1184 KB/s (310584 bytes in 0.256s) Pulled /system/lib/libc.so from device/emulator. Set up JDB connection, using jdb command: C:\Program Files\Java\jdk1.8.0_66\bin\jdb.exe ## COMMAND: adb_cmd forward tcp:65534 jdwp:9139 --------------------./obj/local/armeabi-v7a/gdb.setup--------------- GNU gdb (GDB) 7.7 Copyright (C) 2014 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "--host=i586-pc-mingw32msvc --target=arm-linux-android". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://source.android.com/source/report-bugs.html>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word". Remote debugging from host 9.11.5.0 warning: Could not load shared library symbols for 112 libraries, e.g. libstdc++.so. Use the "info sharedlibrary" command to see the complete listing. Do you need "set solib-search-path" or "set sysroot"? 0x400daa80 in __futex_syscall3 () from D:\Android\AndroidNDK\android-ndk-r10e\samples\hello-jni\obj\local\armeabi-v7a\libc.so (gdb)點評:該工具要求環境極為苛刻且不穩定,不建議使用
?
Gdb-Gdbserver
操作步驟:
- Android studio導入jni工程,
- 拷貝.so到搜索路徑,pull /system/lib到搜索路徑,pull /system/linker到搜索路徑
- 啟動gdbserver (具體命令根據版本不同而變)
gdbserver --attach *:111 1234 - 轉發端口
adb forward tcp:111 tcp:111 - 連接本地調試器
target remote 127.0.0.1:111
?
?
3.3 無源碼調試 So
?
使用 Arm 版 Gdb 在移動端直接調試
- 獲取arm版gdb
- 把gdb下載到移動端
adb push gdb /data/bin - 執行gdb
adb shell
./data/bin/gdb
點評:該方法速度快,但不好查看符號
?
IDA調試
- 將android_server拷入/data/local/tmp/
adb push android_server /data/local/tmp/ - 修改可執行權限,運行
cd /data/local/tmp/
chmod 755 android_server
./android_server - 將模擬器端口轉發至pc端口 (另開啟命令行)
adb forward tcp:23946 tcp:23946 - IDA中選擇Remote ARMLinux/Android debugger,端口23946,調試即可,成功以后顯示
Accepting connection from 127.0.0.1...
?
Gdb-Gdbserver
- 啟動server
./gdbserver –attach :1234 [pid] - 轉發端口
adb forward tcp:1234 tcp:1234 - 啟動client
arm-linux-androideabi-gdb.exe - 連接server
target remote :1234 - 設置單步調試
set step-mode on - 設置反匯編模式
set disassemble-next on - 設置加載so斷點
catch load 1.so
?
Gikdbg
GikDbg 是一款移動平臺的匯編級調試器,它基于 OllyDbg ,GDB 以及 LLVM 實現而來。OllyDbg 現已廣泛用于 PC 平臺軟件安全領域,GikDbg 是 OllyDbg 向移動平臺轉移的產物,它可以協助您完成諸如應用調試分析,應用安全評估,應用漏洞挖掘等移動安全領域。What features can GikDbg support? http://gikir.com/product.php
- ELF / Mach-O executable file static analysis;
- Android / iOS App dynamic debugging;
- Android / iOS remote console;
- ARM assembler;
- ARM disassembler;
- Device file uploading and downloading;
- Built-in GDB and LLDB;
- Support for memory breakpoint, software breakpoint, conditional breakpoint;
- Support for multi-threaded debugging;
- Support for assembly code level file patching.
?
GikDbg for IOS
?
GikDbg for Android
gikdbg.art-Gikir Debugger for Android RunTime, 是Android平臺的32位匯編級調試器。此處的Android RunTime既指DVM RunTime又指ART RunTime,因此不管是運行dalvik虛擬機還是運行本地代碼的art均可以使用gikdbg.art進行程序的二進制調試分析。不同之處在于dalvik虛擬機的運行時只能調試so動態庫,而art運行時不僅能調試so動態庫,還能調試系統鏡像oat,可執行程序dex這樣的文件。另外,gikdbg-Gikir Debugger for iPhone OS,是調試越獄蘋果設備的32位匯編級調試器,同學們莫搞混淆了哈,它需要一些復雜點的服務端和客戶端的配置,而gikdbg.art在正常情況下是不需要手工配置的,所以別去找android server了。對于靜態分析,可以執行/ART Debug/View/ELF Data…,/ART Debug/View/ELF Code…兩個菜單打開本地so,oat,dex文件。
?
調試 so
Step 0.前置說明
Step 0.前置說明 手機端:Android模擬器,Android 4.4.2 ART 運行時;(真機與DVM運行時是一樣的) PC端:ParallelDesktop虛擬機,Windows 8.0,gikdbg.art v1.0.build140601.3; PS:非root環境的設備由于權限的原因會有很多問題,不推薦使用!Step 1.連接設備
Step 1.連接設備運行模擬器,打開gikdbg.art.exe,執行/ART Debug/Device菜單,我們就可以來到如下界面:如果模擬器已經運行了,但是設備列表中沒有,則等待一段時間后執行右鍵的Refresh菜單。然后雙擊或者右鍵Login就可以登陸選中的設備了。對于第一次Login該設備,會詢問你是否上傳依賴的文件到/data/local,這一步如果否定了的話將不能使用調試功能。上傳文件這個步驟目前已知的問題是對于非root的設備,往往因為權限的原因上傳不成功,一般情況下/data/local/tmp目錄沒有問題,但是有些設備又沒有/data/local/tmp目錄,因此我們只有設置/data/local為目標路徑,這個問題目前還不知道好的解決辦法。歸納一下就是:非root的機器無法在其/data/local下創建我們依賴的文件夾以及上傳文件,如果我們將其遷移至/data/local/tmp這個目錄下,又有部分設備沒有這個文件夾,就更沒有辦法上傳了。
對于這類上傳失敗的同學,可以想辦法手工將$(GIKDBG.ART)/adb/android/gdb傳至/data/local/gikir_android-xxxx/gdb這個位置,其中xxxx是GUID。
如果還沒有安裝該apk文件的,則可以在ADB Shell中執行$install –r命令選擇gikdebugee.apk進行安裝.
?
Step 2.選擇進程
Step 2.選擇進程 登陸成功后執行,確保模擬器的gikdebugee.apk運行正常, 然后執行/ART Debug/File/Attach就可以得到如下進程列表, 選中我們的gikdebugee進程,雙擊或者執行Attach按鈕之后我們就會看到如下加載輸出:
等 gdb 加載完畢之后我們就可以進入熟悉的 CPU 主窗口了:
?
Step 3.選擇模塊
Step 3.選擇模塊 我們的目的是調試apk里面的so動態庫,因此執行/ART Debug/View/Module切換到模塊列表, 選中我們要調試的模塊,雙擊它?
Step 4.擊中斷點
Step 4.擊中斷點 本例中找到要調試的函數getNativeString,我們可以用CTRL+F查找到它, 找到之后F2下斷點,F9運行它,然后在設備中操作按鈕則該方法將被斷點擊中,F8運行3步?
?
3.4 調試 Android 上 Linux 程序
?
adb push %NDK%\prebuilt\android-arm\gdbserver\gdbserver /system/bin chmod 777 /system/bin/gdbserver adb push test.out /system/bin chmod 777 /system/bin/test.out gdbserver :2345 /system/bin/test.out(若附加調試則提供進程號) adb forward tcp:2345 tcp:2345 gdb > gdb > target remote :2345?
技巧:如何在 so 入口下斷?
用 ida分析 so,并在 JNI_OnLoad 下斷點,動態附加后,ida 會自動 rebase,使用 gdb 的 catch load 命令捕獲
?
?
4. Java層 / Linux層 聯合調試?
?
4.1 有源碼聯合調試
參照前幾節
?
4.2 無源碼聯合調試
?
操作步驟
adb shell am start -D -n com.example.hellojni/.HelloJni 啟動app并等待調試器ps | grep hellojni 得到PID 3569 adb shell run-as com.example.hellojni /data/data/com.example.hellojni/lib/gdbserver +debug-socket --attach (3569)PID將PID與文件映射建立調試鏈接(c層) adb forward tcp:5039 localfilesystem:/data/data/com.example.hellojni/debug-socket將調試鏈接和本地端口建立鏈接(c層) adb forward tcp:65534 jdwp:(3569)PID 將本地端口和進程建立連接(java層) jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=65534 使用jdb調試java層 arm-linux-androideabi-gdb.exe target remote :5039 使用gdb調試c層set breakpoint pending on?
最簡單的 gdb 中,斷在加載 so 時刻的方法
-
1. 以等待模式啟動
am start -D -n com.example.hellojni/.HelloJni -
2.Gdbserve r鏈接該進程(ps | grep hello)
gdbserver --attach :1234 10863 -
3.轉發端口
adb forward tcp:1234 tcp:1234 -
4.連接gdb
root@ja3gchnduos:/ # am start -D -n com.example.hellojni/.HelloJni Starting: Intent { cmp=com.example.hellojni/.HelloJni } root@ja3gchnduos:/ # ps | grep hello u0_a165 10863 3593 869292 16088 ffffffff 40077a08 S com.example.hellojni root@ja3gchnduos:/ # gdbserver --attach :1234 10863 Attached; pid = 10863 Listening on port 1234
arm-linux-androideabi-gdb Target remote :1234 -
5.設置符號路徑(提前把/system/lib/*.so /system/bin/linker libhello-jni.so拷貝到目錄)
set solib-search-path c:/1 -
6.設置加載so斷點
catch load libhello-jni.so -
7.執行continue,使用android studio的attach使程序繼續運行
-
8.加載so時自動斷下:
Catchpoint 1 Inferior loaded C:\Users\lichao\sumsing\libhello-jni.so 0x40036b8c in rtld_db_dlactivity () from C:\Users\lichao\sumsing\linker -
9.用ida分析出onload要下斷點的偏移,b *addr下斷
?
4.3 Android linux 內核層調試
Android底層為linux層,gdb用于調試linux應用層,而kgdb用于調試linux內核層
kgdb的android版本下載:http://github.com/dankex/kgdb-android
?
?
5. 使用 Hook
?
5.1 常用 Hook / Inject 工具簡介
常用 Hook 框架:
- Cydia Substrate
? ? ? ? 支持 Java 層 hook
? ? ? ? 支持 Jni 層 hook
? ? ? ? 需要 Root,且機型適配
? ? ? ? 支持 dalvik,不支持 art
? ? ? ? 閉源 - Xposed?https://github.com/rovo89/Xposed
? ? ? ? 支持 Java 層 hook
? ? ? ? 需要 Root,且機型適配
? ? ? ? 支持 dalvik/art
? ? ? ? 開源 - Frida https://github.com/frida/frida
? ? ? ? 需要 Root
? ? ? ? 支持 Java 層 hook
? ? ? ? 支持 Jni 層 hook
? ? ? ? 支持 dalvik/art
? ? ? ? 開源
? ? ? ? 任意時刻注入,簡單易用,遠程代碼即時編譯并注入運行 - Adbi https://github.com/evilsocket/arminject
? ? ? ? 需要 Root
? ? ? ? 支持 Jni 層 hook
? ? ? ? 任意時刻注入,手工
? ? ? ? 開源
?
?
6. 實例:360手機衛士卸載后彈窗分析過程
?
6.1 現象
360手機衛士在非root情況下卸載后彈出瀏覽器。于是有2種常見可能,一種是intent跳轉,一種是執行am命令,后者可以在java層和jni層實現,如果是java層考慮進行hook,jni層考慮修改am.jar
?
6.2 文件注入
將/system/bin/am改名,發現無法彈窗,于是確定是通過第二種方式實現,為了確定調用層級,嘗試修改(反編譯成smali->加入logcat輸出打印回溯棧和接收參數->回編譯)/system/framework/am.jar,(可以通過自己再另一個app中實現同樣的功能,通過反編譯得到smali代碼)再次反編譯后內容如下:
public static void main(String[] args) {String v0 = "";int v3 = args.length;int v2;for(v2 = 0; v2 < v3; ++v2) {v0 = String.valueOf(v0) + " " + args[v2];}Log.d("my god", v0);Log.d("my god", Log.getStackTraceString(new Throwable()));}?
分析日志
在卸載瞬間拿到輸出:
start -n com.android.browser/.BrowserActivity -a android.intent.action.VIEW -d http://shouji.360.cn/web/uninstall/uninstall.html?u=100&id=76bb84de8f53b53f57dd3cedfe966091&v=6.3.1.1048&s=1&model=SE0gTk9URSAxTFRF&sdk=19&ch=200222&wid=9fa298f35aec4232c26048442f36dc59 --user 0 java.lang.Throwable at com.android.commands.am.Am.main(Am.java:30)at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:245)at dalvik.system.NativeStart.main(Native Method)發現命令行是 am start –n com.android.browser/.BrowserActivity -a android.intent.action.VIEW
?
定位關鍵代碼
通過字符串搜索,定位到java層關鍵代碼,使用android hook框架cydia substrate,掛鉤java.lang.Runtime類的exec函數,定位到調用棧:
content:/data/user/0/com.qihoo360.mobilesafe/files/so_libs/um.0.2 com.qihoo360.mobilesafe --execute am start -n com.android.browser/.BrowserActivity -a android.intent.action.VIEW -d http://shouji.360.cn/web/uninstall/uninstall.html?u=100\&id=7b55c26b779bd111dfed8b02bb00131c\&v=5.5.0.1041\&s=1\&model=TmV4dXMgUw\&sdk=19\&at=KTvooEkhHMzgJ13AXfMkJINnhrmyyNdu\&ch=200222 --user 0 java.lang.Throwable at com.example.emptytest.Main$1$1.invoked(Main.java:68)at com.saurik.substrate.MS$2.invoked(MS.java:68)at java.lang.Runtime.exec(Native Method)at egv.a(360MobileSafe:257)at egv.a(360MobileSafe:66)at com.qihoo360.mobilesafe.ui.index.MobileSafeApplication.p(360MobileSafe:1223)at com.qihoo360.mobilesafe.ui.index.MobileSafeApplication.onCreate(360MobileSafe:799)?
結論
啟動不久,360啟動 linux 程序?/data/data/com.qihoo360.mobilesafe/com.qihoo360.mobilesafe/files/so_libs/um.0.2,并將彈窗任務以參數形式傳遞給該程序,程序中對 /data/data/com.qihoo360.mobilesafe 文件夾的刪除操作進行掛鉤,以實現卸載后彈窗機制
?
?
7. GDB 調試?
?
7.1 反匯編一段地址
(gdb) disass /r 0x401148b8,0x40114900 Dump of assembler code from 0x401148b8 to 0x401148c8: => 0x401148b8: 0c 70 a0 e1 mov r7, r120x401148bc: 01 0a 70 e3 cmn r0, #4096 ; 0x10000x401148c0: 1e ff 2f 91 bxls lr0x401148c4: 00 00 60 e2 rsb r0, r0, #00x401148c8: 0e 70 00 ea b 0x40130908 End of assembler dump.7.2 表達式計算
print exprprint ”%d” adprintf 動態插入printf函數dprintf location,format string,arg1,arg2,...7.3 查看寄存器
info registers
7.4 查看棧參數
info args
7.5 查看局部變量
info locals
7.6 查看內存
x
7.7 修改內存
set *(unsigned int*)0x800000000=0x000000007.8 斷點
break [PROBE_MODIFIER] [LOCATION] [thread THREADNUM] [if CONDITION]
clear [LOCATION]
一次斷點
tbreak [PROBE_MODIFIER] [LOCATION] [thread THREADNUM] [if CONDITION]
條件/線程斷點
break func1 thread1 if i==0
觀察斷點
watch/awatch/ rwatch [-l|-location] EXPRESSION 變化/讀寫/讀斷點 (如果EXPRESSION不是絕對地址,則需要用-l計算表達式) watch *0x40000000==0x90909090 watch –l *$pc watch i (有源碼,變量i的值有變化時停止)范圍斷點
break-range START-LOCATION, END-LOCATION break-range 1.c:5, 1.c:10 在1.c的第5行和第10行之間下斷 break-range +5, +10 在當前行+5和當前行+10之間下斷硬件斷點
普通硬斷 hbreak [PROBE_MODIFIER] [LOCATION] [thread THREADNUM] [if CONDITION]
臨時硬斷 thbreak [PROBE_MODIFIER] [LOCATION] [thread THREADNUM] [if CONDITION]
攔截當前函數退出
xbreak
捕獲斷點
普通補斷 catch [assert|catch|exception|exec|fork|load|rethrow|signal|syscall|throw|unload|vfork]
臨時捕斷 tcatch [assert|catch|exception|exec|fork|load|rethrow|signal|syscall|throw|unload|vfork]
跟蹤斷點
strace [LOCATION] [IF CONDITION]
7.9 查看調用棧
bt [N] 顯示N層調用棧
bt full 顯示全部調用棧
7.10 流程控制
| 運行時中斷 | Ctrl+C |
| 結束程序 | kill |
| 單步步過 | next |
| 單步步入 | step |
| 單步步過(指令級) | nexti |
| 單步步入(指令級) | stepi |
| 繼續運行 | continue |
| 執行到當前函數指定位置 | advance |
| 分離進程 | detach |
| 強制跳轉 | jump |
反向調試
reverse-continue reverse-next reverse-search reverse-stepi reverse-finish reverse-next reverse-step
7.11 顯示當前加載模塊
info shared
7.12 強制加載模塊
可以用于做二進制對比
(gdb) load C:/Users/lichao/2/libadnative.so 0x50000000 Loading section .interp, size 0x13 lma 0x50000134 Loading section .dynsym, size 0x1210 lma 0x50000148 Loading section .dynstr, size 0x2061 lma 0x50001358 Loading section .hash, size 0x8a8 lma 0x500033bc Loading section .rel.dyn, size 0x10a0 lma 0x50003c64 Loading section .rel.plt, size 0x1b0 lma 0x50004d04 Loading section .plt, size 0x29c lma 0x50004eb4 Loading section .text, size 0xe7a0 lma 0x50005150 Loading section .ARM.extab, size 0x8e8 lma 0x500138f0 Loading section .ARM.exidx, size 0xd80 lma 0x500141d8 Loading section .rodata, size 0x11bc lma 0x50014f58 Loading section .data.rel.ro.local, size 0x738 lma 0x50018098 Loading section .fini_array, size 0x8 lma 0x500187d0 Loading section .init_array, size 0x14 lma 0x500187d8 Loading section .data.rel.ro, size 0x508 lma 0x500187f0 Loading section .dynamic, size 0xf8 lma 0x50018cf8 Loading section .got, size 0x210 lma 0x50018df0 Loading section .data, size 0x1c lma 0x50019000 Start address 0x0, load size 94044 Transfer rate: 188 KB/sec, 2541 bytes/write.7.13 替換當前調試模塊
file c:/1.so7.14 進程轉儲
gcore
7.15 進程空間
進程空間 inferior,用于調試多個進程,fork函數會自動添加進程空間
| 添加進程空間 | add-inferior |
| 復制進程空間 | clone-inferior 1 |
| 刪除進程空間 | remove-inferior 1 |
| 切換進程空間 | inferior 2 |
| 分離進程空間 | detach inferior 2 |
7.16 由地址獲對應的符號
maintenance translate-address [address]
7.17 查找符號
info functions [regex] 定位地址 info symbol address 定位文件 info variables [regex] 全局靜態符號7.18 執行外部命令
目標系統:! [command]
主機系統:shell [command]
7.19 顯示線程
info threads
7.20 打印c++對象虛表
info vtbl
抄自此博客
?
?
總結
以上是生活随笔為你收集整理的Android 逆向分析大全的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CompletableFuture详解~
- 下一篇: 安卓逆向_15( 三 ) --- An