動態分析Android App之動態調試


動態分析Android App之動態調試 

來源 https://juejin.im/entry/6844904168826601486

參考 https://blog.csdn.net/qq_38851536/article/details/100026480

 

這個系列一共有五篇左右,內容主要介紹如何在Java層動態分析和調試Android App,和網上其他教程相比,內容更充實,體系更健全,深入而淺出。
聞道有先后,術業有專攻,希望能給剛入門Android逆向的同儕們些微幫助。出於各種原因,文章有兩個遺憾,一是只包含了Java層代碼的動態分析和調試,Jni和Native層並沒有涉及;二是對Hook框架的介紹和使用不是很充分,因為Hook值得另外很多個五篇去寫。逆向太深太廣了,吾輩將上下而求索。

本篇內容所涉及到的資源
鏈接:https://pan.baidu.com/s/14ZF-7pop4NbrDPydtRQOeg
提取碼:8fs8

一、認識動態分析Android程序

我們將需要運行應用程序才能實施和完成的分析方法統稱為動態分析方法,不要被這個名詞嚇到了,抓包、動態調試、觀察App頁面的UI設計和交互、使用Xposed/Frida Hook App中某個函數、Smali插樁等等,這些都可以稱為動態分析,有時候我們也會認為Smali插樁是一種靜態的技術,但這里不用過分區分和計較,我們最應該關注的是技術和思路。
這個系列會逐一講解和介紹這些動態分析工具的使用,為了防止我講的不夠清楚,每一個知識點和技術都會配上數篇詳細可靠的同類型文章。當然,你也可以直接Google獲取這些知識,但需要稍加甄別搜索到的內容。我們第一篇說一下Smali動態調試。

1.1 什么是Smali動態調試?

調試分為源碼級調試和反匯編級別調試,源碼級調試是什么自然不用說,程序員大多都使用過諸如Pycharm這樣的IDE對自己的程序進行過源碼級調試,從而了解程序運行情況,分析程序的執行流程,觀察變量的動態值等。而我們進行逆向分析時,手里不可能有App的源碼,這就需要進行反匯編級的調試,也就是我們常說的的Smali動態調試。

當我學習Smali時,產生過各種各樣的困惑,smali是什么?我寫的是java代碼,怎么變成smali了?為什么可以smali代碼可以調試?Jadx反編譯出來的java代碼如此優雅,能不能根據這些代碼進行源碼級調試? 我們一一來探討這些問題。

從Java源碼到編譯打包成APK文件,會經過非常復雜和繁多的步驟,我們這里只關注代碼的編譯過程。

Android平台上主要使用Java語言來開發程序,但Android上的程序運行機制和標准的Java程序並不一樣。 我們先看一下Java程序從編寫到執行的過程

第一步:編寫Java代碼
第二步:所有的Java代碼通過Java編譯器被編譯成java字節碼,即.class文件
第三步:Java字節碼在Java虛擬機上被解釋成機器語言后,程序執行

手機系統內存和處理器速度有限,為了解決運行效率的問題,以及擺脫和Java母公司的版權糾紛,Google開發了Dalvik虛擬機,Android程序就運行在其上,我們來看一下App中Java代碼的編譯過程。

第一步:編寫Java代碼
第二步:所有的Java代碼通過Java編譯器(javac)被編譯成Java字節碼,即.class文件
第三步:Java字節碼通過Android 的dx工具轉換為Dalvik字節碼,即.dex文件
第四步:Dalvik字節碼在Dalvik虛擬機上運行

現在我們已經知道解壓Apk后,那些奇怪的dex是哪來的了,那Smali又是哪來的呢?
我們看一下Smali代碼長啥樣

首先澄清一下這兩個概念

反匯編:將可執行的文件中的二進制經過分析轉變為匯編程序。 反編譯:將可執行的程序經過分析轉變為高級語言的源代碼格式,一般完全的轉換不太可能,因為有編譯器的優化等因素在里面。

各種各樣反編譯工具幾乎都用到了Baksmali這個工具,我們來看一下它和孿生兄弟的介紹:

Smali,Baksmali分別是指安卓系統里的Java虛擬機(Dalvik)所使用的一種.dex格式文件的匯編器,反匯編器。
其語法是一種寬松式的Jasmin/dedexer語法,而且它實現了.dex格式所有功能(注解,調試信息,線路信息等)。
Smali,Baksmali分別是冰島語中編譯器,反編譯器的叫法。

也就是說,Smali代碼是利用Baksmali反匯編(disassemble)dex文件得到的一種類匯編代碼,它完整的實現了.dex格式所有功能(注解,調試信息,線路信息等),談起Smali和dex之間的關系,我們常常稱為轉化(convert),即還原度極高,這也是Smali可以勝任動態調試的重要原因。而利用Smali匯編器,我們可以修改Smali代碼,重新編譯成dex文件,進一步可以對Apk進行重打包。

在黑產中,這也是無數盜版應用、破解版應用、功能增強應用、去廣告版應用的實現原理。

而我們這些只是想分析App通信協議的程序員,也可以簡單的修改Smali進行“Smali插樁”,通過log探針輸出一些可疑的信息,或者根據探針是否觸發,探測程序運行邏輯。

提到Smali插樁,玩法也不少,Android逆向人員在仰望星空時,或多或少都渴望過一種破解App的暴力美學——讓每一行代碼都自動吐出來一句話“爺,我在這兒,我是干嘛的,我前面又是啥。” 換成可以通過代碼實施的方案,也就是在每個方法內打印調用棧或者log輸出一下它在哪個方法里,該怎么做呢?方法非常多,我們看一下暴力插樁的三個思路。

1.直接對smali代碼進行文法分析,寫一些正則表達式的判斷,實現在每行代碼后面加上log輸出的Smali代碼,然后使用Smali匯編器編譯成dex文件,進而重打包Apk,最后運行App查看log輸出。具體實現可以參考這篇文章,實現出來的效果也很不錯實現出來的效果也很好。
《Android應用逆向——分析反編譯代碼之大神器》 https://blog.csdn.net/charlessimonyi/article/details/52027563

2.直接對Dex進行操作,可以使用的工具有ReDex,Dexter等。但由於Dalvik發展尚淺,且由於Dalvik字節碼比Java字節碼的結構更加緊湊,所以修改起來比較復雜,筆者暫時沒有看到逆向中應用Dex插樁的具體實現。

3.既然Dex緊湊而且不好搞,那能不能搞Java字節碼進行插樁呢?答案是完全可以的,對Java字節碼進行操作的工具非常多且成熟,可以通過AspectJ、Asm、javassit等工具對Java字節碼進行操作。下面這個工具,就是將Dex文件轉換為Java字節碼,再使用asm操作字節碼,最后再用dx工具(Android Java代碼編譯流程第三步中提到的Android自帶工具)編譯成dex,進而重打包Apk,最后運行App查看log輸出。
《帶你開發一款給Apk中自動注入代碼工具icodetools》
https://blog.csdn.net/jiangwei0910410003/article/details/53386071

Smali和Smali插樁就介紹到這兒了,感興趣的同學可以去試一下。 講道理,我們應該先花一萬字講一下smali語法和smali如何實現基礎的插樁,但動態調試Smali實在是方便又迷人,我發誓,在掌握Smali動態調試后,你們很快就會將又麻煩又容易出錯的Smali插樁忘在腦后。

但這絕不意味着我們就不需要能看懂和理解Smali語法了,原因主要有兩個

  1. Smali動態調試基於Smali代碼,斷點該下在哪里,程序跑到哪兒了,如何查看調試中的變量值……這些都需要你稍微懂一些Smali語法。
  2. Jadx之類的工具雖然號稱做到了Dex到Java的一站式反編譯(Dex to Java decompiler),但在其內部,是先將Dex反匯編成smali,再用asm將smali轉成class字節碼,最后解析查看Java代碼。如果說Dex到Smali是一種convert(轉化),得到的是可調式、精准靠譜的源碼,那么Dex到Java就是一種translation(翻譯),雖然看着可能還不錯,但其實和源碼相差甚遠,只能稱為Java偽代碼,正因為相差甚遠,所以我們才無法使用Jadx反編譯出來的java代碼進行源碼級的調試,而只能進行稍顯晦澀的Smali調試。所以你可能會在使用Jadx反編譯過程中遇到部分邏輯無法翻譯成java代碼的情況,或者翻譯的Java代碼和真實的Smali邏輯有差異,這個時候就需要老老實實看Smali代碼。

在下一篇中,我們將結合小紅書應用來講解Smali的語法,這一篇的主角還是實現如何進行動態調試。

1.2 為什么要進行Smali動態調試?

動態調試能更充分的展示程序的運行邏輯,簡而言之倍兒爽。

1.3 不能進行Java調試嗎?

在上面我們已經講的很清楚了,反編譯得到的Java代碼只是一種翻譯而來的偽代碼,它無法支撐其源代碼級的動態調試。

接下來我們開始動態調試Smali之旅。

二、推開調試之門

出於安全考慮,Android系統並不允許應用被隨意調試,官方文檔稱需要滿足二者之一的條件。

1.App的AndroidManifest.xml中Application標簽必選包含屬性android:debuggable=“true”; 2./default.prop中ro.debuggable的值為1;

我們先來看第一個條件有沒有辦法滿足,首先,發行版的App都會將debuggable設置為 false,使第三方不能直接調試分析APP,這也是廠商出於安全的考慮,那我們就需要反編譯Apk,修改后進行重打包,這也是絕大多數教程的做法,但我個人非常非常不建議這么操作,因為重打包容易遭受無妄之災,這也是我不喜歡Smali插樁的原因——它們需要重打包App。
你想研究App的通訊協議和加密字段,這已經足夠讓人焦頭爛額,你可能會遇到繁雜的代碼、詭異的反抓包,So層的加密……而如果你對App進行重打包,那就要面對App額外的保護措施,比如重打包失敗,簽名驗證等。因為重打包這個操作主要是開發盜版App和破解版App做的事,這對廠商來說更加難以忍受,只是修改一個debuggable字段就要攬上這么多事,顯然吃力不討好。

那第二個條件好滿足嗎?default.prop 文件非常好找,它就在Android的根目錄下,我們可以通過ES文件瀏覽器找到它。

很不幸的發現,這台手機的debuggable標識為0,不可調試。一個朴素的想法是直接修改這個值不就可以了?但是這是不可以的,這個值只在系統啟動時,也就是開機時才會讀取和加載一次。那重啟?抱歉,每次重啟,這個值就會恢復默認。所以就造成了一個死循環。
那我們是怎么解決它的呢?有這樣幾種辦法。
1.改寫系統文件,修改ro.debuggable為1,重新編譯系統鏡像文件,刷入設備。
難度稍大,但一勞永逸,缺點是對新手很不友好。可以參考這篇文章,https://bbs.pediy.com/thread-197334.htm。
2.注入init進程,修改內存中ro.debuggable的值,這個也是之前慣常的做法。
通過大佬寫的mprop工具可以修改內存中所有的屬性值,只需要按照操作步驟,cmd敲七八行即可,還有人出了一鍵式的bat腳本。資源放在了我分享的百度資源里,大家也可以去制作者那兒下載。https://bbs.pediy.com/thread-223294.htm。 需要注意的是,因為是修改內存值,所以文件中的ro.debuggable值並不會變化,且每次重啟設備都要重新注入。
3.使用開發版/測試版的手機系統,ro.debuggable值常常為1
4.使用模擬器,比如雷電模擬器、Genymotion等,許多模擬器天然支持動態調試,盡管defalut.prop中值並不為1,打開adb shell,用getprop ro.debuggable命令查看內存中的debuggable值卻為1。

5.Xposed Hook系統判定函數,Android系統憑什么判斷某個App是一個可調試的應用?從讀取AndroidManifest.xml中android:debuggable屬性值,到打開這個應用,里面有非常多的門路,找一個合適的時機進行hook,就可以實現狸貓換太子,這需要逆向分析人員了解Android源碼,我們這里不去說它,因為成熟的工具已經有很多了,只需要下載Apk,在Xposed中激活后重啟手機,就可以一勞永逸。

  • BDOpener 開啟APK調試與備份選項的Xposed模塊 https://security.tencent.com/index.php/opensource/detail/17
  • Xinstall 如何不重打包調試Android應用 https://www.open-open.com/lib/view/open1426304176732.html
  • BuildProp Enhancer Make all application attribute android:debuggable=“true” https://repo.xposed.info/module/com.jecelyin.buildprop
  • XDebug make all app on your phone debuggable https://github.com/deskid/XDebug

我個人平時用BDopener,網盤資源中存放了數種開啟調試的工具,請自行選擇,Xinstall是個十分優秀的Xposed框架,我們日后還會用到它。

三、選擇調試目標

我猜測你很可能選擇了雷電模擬器,或者在真機上裝了BDOpener,我並不覺得意外,因為這兩種方法確實最為便捷,之所以我們要講那么多種方法,是為了避免意外,有的機型或者有的App存在閃退行為,這樣你就可以求助於另外一個方法。

我們演示在雷電模擬器上調試新浪博客的一個sign參數,不難,但又不是紙玩具,非常適合我們進行測試。
下載新浪博客最新版,我在百度雲里也放了apk。開啟Fiddler/Charles抓包工具后,打開App。

點開一條博客

查看Fiddler,多出四五條數據,根據數據包大小和內容找到我們需要的那一條。

這是一個GET請求,字段有九個

deviceid、chno,appver,appid是固定不變的,Is_default可以不填,login_uid因為沒有登錄也不用管,article_id是每篇文章的id,顯然這個sign是比較好玩的。
它是64位十六進制數,猜測是兩個md5拼接,不太確定哦。

我們接下來使用Jadx反編譯Apk,搜索url鏈接的末尾部分,即get_article_info.php,放一張之前教程的截圖

只有m字符串是符合要求,雙擊代碼進去看一下,在這個config(配置)包里,以類變量的方式存放着大量的字符串,如果想引用它,就是b.m

右鍵查看用例,看一下這個url在哪兒被使用了,發現只有一處

雙擊第二行的代碼,查看詳細引用

它包裝了一個a方法來取我們的目標URL,再次查找用例

點開第一個,你會發現其實它就是a方法上面的那個方法

看到這些代碼你應該感到喜聞樂見了,我們發送網絡請求,首先就要進行字段的獲取和拼接,在Java中往往由集合map完成,格式類似於{”id“:3,“name”:“lilac”},put存入,get取出,這兒就是一個典型的Hashmap。

它第一步初始化一個map,之后巴拉巴拉放進去很多東西,看着和我們get請求中的字段一致。接下來我們用Smali動態調試跟蹤一下集合m從初始化到塞滿東西的全部過程。
首先我要說明,這兒不用動態調試也是完全可以的,但App並不總是很簡單,可以一目了然。

首先我們要獲取反編譯的Smali代碼,因為我們的調試就是基於Smali的。你可以使用Apktool敲幾行命令完成,但我個人更喜歡可視化的界面,市面上有很多集成了這些工具,可視化拖拽操作的工具。
我這邊演示windows下的操作,工具放在了網盤里,也可以自行搜索下載。

操作選擇反編譯apk——拖拽apk到源文件——點擊操作,依據電腦性能和Apk的大小,反編譯所需時間要幾十秒到十分鍾不等,反編譯完成后自動彈出文件目錄。

mac可以下載這個工具https://github.com/Jermic/Android-Crack-Tool ,界面和操作幾乎和windows中一樣。

都講這么久了,我們都還沒說調試工具,是這樣的,幾乎所有的主流Java IDE配上smalidea插件都可以對Smali進行動態調試,除此之外,JEB也可以直接調試Smali,IDA也有調試DEX的能力,還有Qtrace等等工具,但調試Smali,我只推薦Android Studio+smalidea插件這個組合,操作簡單,功能強大,效果也很穩定。

接下來打開Android Studio(注:Android studio版本需要大於3.0,我個人是3.5Beta版)
先下載smalidea插件,可以直接用我的百度雲鏈接,也可以去官網下載 https://bitbucket.org/JesusFreke/smali/downloads/

Android Studio–>Settings–>Plugins–>Install plugin from desk…,安裝插件;需要注意smalidea路徑最好不要有中文路徑,可能會出問題。安裝好后重啟生效。

打開項目【Open an existing Android Studio Project】,選擇sinablog文件夾,等待其加載,過程會持續數分鍾。

加載完成之后,你需要配置一下JDK和SDK,SDK並不一定要和我一樣,29,22……或者別的其他版本都可以。

我們要在想要跟蹤的程序起始處下斷電,重新上一下圖,它是類com.sina.sinablog.network.d中的一個a方法,我們要在smali文件夾中找到它。

在目錄中,我們可以看到兩個smali文件夾,這是由於dex分包造成的,你暫時可以不用理解它,smali1文件夾找不到對應的包,就去下一個找即可,注意要切換到Project目錄。

接下來找到a方法,我們需要了解Smali語法才能讀懂它,這一部分我打算下一節結合Smali插樁講,大家也可以自行搜索和學習。
我們現在只需要知道".method "和“.end method”分別是方法開始和結束的地方即可。下圖紅框即a方法的兩個重載方法,它們對應着我們前面分析的Jadx偽代碼。

對比一下Jadx反編譯的結果

顯然Smali代碼更長的那個才是我們需要的重載方法,在代碼左邊空白處單擊即可下斷點。

下好斷點后,就可以准備開始運行了。
首先,紅框一圈出來的設備信息處,必須要有一個藍色的設備在運行,如果你的是黑色,可以重啟一下模擬器。

確定設備沒問題后,點擊那個帶箭頭的小蟲子

我們現在要找到我們應用的包名,查看應用包名以及其他信息的方法和工具非常多,我這里推薦一個非常優雅的工具Apk Messenger,在反編譯的第一步,我們需要對應用進行查殼,我也建議使用它,因為它實在是難得的UI設計好看的反編譯工具。這是它的官網,https://www.ghpym.com/apkinfo.html ,百度雲也放了相應的資源。

直接拖拽Apk 進去

細心的小伙伴可能會發現,它判定應用進行了騰訊加固,這似乎是一個誤報。這是為什么呢?事實上,Apk的查殼工具都並不算聰明,是通過檢查Apk目錄中是否由加固軟件的特征文件判斷的,我們來看一下APK Messenger的判斷庫。

再用360壓縮或者別的壓縮工具打開Apk,會在assets目錄下找到這些。

所以不用去管它,我們反編譯內容正常,就不用考慮它為什么是個“假加固”的事了。
現在我們知道了包名,在模擬器中打開App,在列表中找到com.sina.sinablog附加調試即可,反復調試Smali代碼時,可能會出現進程列表里沒有這個App的狀況,重啟App即可。

網上很多教程都讓你用DDMS查看端口,再用adb動態轉發端口之類的,其實這些步驟一般都是不需要的。
我們現在已經開始了Smali調試,只需要觸發斷點即可。

點擊一條博客

關於Android Studio調試工具如何使用,網上已經有非常多的文章了,不熟悉的可以看一下這篇文章https://blog.csdn.net/yy1300326388/article/details/46501871

我們這里需要使用到下圖這些功能

我們來看一下Smali代碼,我們下一篇才會講Smali語法,但如果大家先學習了Smali語法,會對Smali調試有非常大的幫助。

運行完move-result-object v0 這一行后,我們在Watches監視器中添加v0,接下來我們就可以一路F8,感受它的變化了。
初始化hashmap時,那個方法已經塞進去五個字段了,一步步F8,你會發現v0里的字段越來越多,沒過多久,Get請求的九個字段就全部躺在了v0中。

光靠F8一行一行走是沒辦法得知的,你可以退出調試模式,更加精細的看一下,F7進入到子方法,Shift+F8跳出方法,這樣子多走幾遍,你就理解了。
在m方法中,獲得了5個字段

出了m方法后,得到了三個字段

我們用Jadx的Java代碼上標記一下

SIGN是怎么生成的呢?

不熟悉Smali可以在Jadx中對應看一下

sign值是由CpltUtil.invoke()方法生成的,參數是兩個字符串,第一個是固定字符串“/apicheck/blog”,第二個參數是八個參數的字符串,我們可以用計算器查看一下。

在如圖這一步,v0即包含了8個字段的map,點擊圖中紅框的計算器,它叫Evaluate Expression,可以在這兒運行各種各樣的表達式
輸入new JSONObject(v0).toString();
你會發現JSONObject飄紅,按照提示進行導包

導包后Evaluate

好吧,報錯,那我們就不轉JsonObject了,直接toString轉成字符串看看什么樣

結果為{is_default=0, login_uid=, blog_uid=1260074450, article_id=4b1b35d20102yvlj, appver=6.1.2, appid=2, deviceid=e9ec21f2f9a7dc8f9c4e10694bdc6143, chno=515_104},和預期一樣。
接下來我們在Jadx中看一下這個CpltUtil.invoke()方法

一看這名字,似乎是個native方法,Ctrl+左鍵進入

竟然是一個native層的加密函數,在之后native層破解時我們再提它,大家可以先試一下。
去lib庫中找到libcrossplt.so文件,在ida中反編譯,這個函數是靜態注冊的,所以很容易就可以在Exports列表中找到它,之后F5反匯編成c代碼,靜態分析c代碼或者ida動態調試即可。

不入so層,就還沒有入門逆向,大家加油,下一篇可能講Smali相關的東西。

 

================= End

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM