最近分析了一個名為xaingce.apk(md5:4bc87096a1c5a33479f1ca8b82f3c130)的惡意apk樣本,其主要惡意行為是竊取使用者隐私,包括通訊錄和短信等。樣本采用了一些對抗靜态分析的技術,本文就如何通過調試發現其對抗的方法進行介紹。分析用到Apktool,Jeb和Eclipse等工具,所涉及到的使用步驟也将會加以說明。
1 問題
通常分析apk樣本,最常用的工具就是Jeb。打開樣本後,首先檢視Manifest,明确Activity等關健元件,以及申請了哪些比較有威脅的權限(如:READ_CONTACTS,READ_SMS)。
其次,可以檢視Resources,看一下是否有感興趣的内容,特别是 values/strings.xml中的字元串。然而這個樣本在Jeb打開後,Manifest标簽中的内容卻是空的,Resources标簽也不存在。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcugDMxgHMwMTLyADMfV2Yn5Wahh3LcJDMvwlNxAjMvw1ckF2bsBXdvwFduVGdu92YtA3dvwFdl5mLzV3YvZ2cu5yZvxmYvw1LcpDc0RHaiojIsJye.png)
xaingce_002.png
Manifest和Resource肯定在樣本檔案中是存在的,那麼在Jeb中沒能顯示出來,一定是解析出了問題。 當然,這其實也不會使分析進行不下去,因為Java源碼還是可以逆出來的。但是出于研究的目的,還是希望能找出其中的原因。
細心的讀者可能會提出疑問,若是AndroidManifest.xml損壞了,這樣的apk還能正常安裝和運作嗎?
對于該樣本,答案是YES。
是以,應該是程式作者利用了Android的這種較強的容錯能力,來對抗分析工具。 這種情況我在之前分析word樣本時也是很常遇到的。
2 搭建調試環境
這裡我們要調試的不是Jeb,而是Apktool,一方面當然是因為Apktool開源,另外Jeb在About的第三方軟體清單中也列有Apktool,是以我猜測Jeb逆向apk用的也是Apktool(未确認)。
2.1 下載下傳和編譯Apktool
- 下載下傳源碼 $ git clone https://github.com/iBotPeaches/Apktool $ cd Apktool
- 應用smali更新檔,建立brut.apktool.smali目錄 $ ./gradlew applyPatches [注] 這一步要聯網下載下傳檔案,需要視網絡狀況,等待一段時間。
- 編譯 $ ./gradlew build fatJar [注] 在我的環境中,build會因為部分test不通過而失敗,此時單獨再執行”./gradlew fatJar”也是可以成功的。
2.2 導入項目到Eclipse中
我用的Eclipse版本為4.4.2,并安裝gradle插件(https://marketplace.eclipse.org/content/gradle-integration-eclipse-0)
- 在File->Import對話框選擇Gradle->Gradle Project
xaingce_003
- 下一步,選擇Apktool源碼所在目錄,執行Build Model,然後選中所有Project
xaingce_004
- 完成導入
3 調試分析
3.1 配置debug參數
- 在Project Explorer中的apktool-cli項目上單擊右鍵,選擇Debug As->Debug Configuration
- 在Java Application條目上單擊右鍵,選擇New
- 選擇Main class,單擊Search,選擇”Main – brut.apktool”
-
配置啟動參數,”d -f -s -o /tmp/xaingce /tmp/xaingce.apk”
參數說明:
d decode
-f Force delete destination directory.
-s Do not decode sources.
-o The name of folder that gets written.
xaingce_011
3.2 第一次試錯
配置好debug參數後,可以直接單擊Debug開始調試,這時可能會提示smali和apktool-lib項目中存在錯誤,忽略即可。 然後,我們會在Console中得到以下的異常資訊:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | Exception in thread "main" brut . androlib . AndrolibException : Could not decode XML at brut . androlib . res . decoder . XmlPullStreamDecoder . decode ( XmlPullStreamDecoder . java : 147 ) at brut . androlib . res . decoder . XmlPullStreamDecoder . decodeManifest ( XmlPullStreamDecoder . java : 153 ) at brut . androlib . res . decoder . ResFileDecoder . decodeManifest ( ResFileDecoder . java : 140 ) at brut . androlib . res . AndrolibResources . decodeManifestWithResources ( AndrolibResources . java : 208 ) at brut . androlib . Androlib . decodeManifestWithResources ( Androlib . java : 140 ) at brut . androlib . ApkDecoder . decode ( ApkDecoder . java : 103 ) at brut . apktool . Main . cmdDecode ( Main . java : 165 ) at brut . apktool . Main . main ( Main . java : 81 ) Caused by : java . io . IOException : Expected : 0x00080003 , got : 0x00080000 at brut . util . ExtDataInput . skipCheckInt ( ExtDataInput . java : 48 ) at brut . androlib . res . decoder . AXmlResourceParser . doNext ( AXmlResourceParser . java : 839 ) at brut . androlib . res . decoder . AXmlResourceParser . next ( AXmlResourceParser . java : 96 ) at brut . androlib . res . decoder . AXmlResourceParser . nextToken ( AXmlResourceParser . java : 106 ) at org . xmlpull . v1 . wrapper . classic . XmlPullParserDelegate . nextToken ( XmlPullParserDelegate . java : 105 ) at brut . androlib . res . decoder . XmlPullStreamDecoder . decode ( XmlPullStreamDecoder . java : 140 ) . . . 7 more |
在此就不賣關子了,很顯然存在一處預期的值是0x00080003,而得到的值是0x00080000,實際上這就是axml格式的前4個bytes,是固定值。
下面來說一下如何用apktool來重打包,然後繼續後面的分析。
3.3 使用Apktool重打包
這個過程在Shell中操作比較友善,在編譯過Apktool之後,我們可以在brut.apktool/apktool-cli/build/libs目錄下得到 apktool-cli.jar,隻要使用”java -jar apktool-cli.jar”就可以執行相應指令行操作了。
-
decode
$ java -jar apktool-cli d -f -r -s -o /tmp/xaingce /tmp/xaingce.apk
這裡比調試時多了”-r Do not decode resources.”參數,可以避免解包出錯。
- 修改AndroidManifest.xml
注意此時的AndroidManifest.xml并不是明文格式,而是axml格式。
我們可以使用010Editor(或其它二進制編輯工具)來修改,将第1個位元組的”00″修改為”03″。
-
build
$ java -jar apktool-cli b -f -o /tmp/xaingce_re.apk /tmp/xaingce.apk
參數說明:
1 2 3 4 | b build - f Skip changes detection and build all files . - o The name of apk that gets written . |
這樣我們得到重打包後的xaingce_re.apk。
3.4 繼續調試分析
現在我們把Debug的啟動參數修改為”d -f -s -o /tmp/xaingce /tmp/xaingce_re.apk”,再次運作。 當然,本應該是不知道會不會再次異常,不過如果運作正常,本文也就沒有再寫下去的必要了:)
此時得到的異常是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | Exception in thread "main" brut . androlib . AndrolibExceptio Could not decode XML at brut . androlib . res . decoder . XmlPullStreamDecoder . decode ( XmlPullStreamDecoder . java : 147 ) at brut . androlib . res . decoder . XmlPullStreamDecoder . decodeManifest ( XmlPullStreamDecoder . java : 153 ) at brut . androlib . res . decoder . ResFileDecoder . decodeManifest ( ResFileDecoder . java : 140 ) at brut . androlib . res . AndrolibResources . decodeManifestWithResources ( AndrolibResources . java : 208 ) at brut . androlib . Androlib . decodeManifestWithResources ( Androlib . java : 140 ) at brut . androlib . ApkDecoder . decode ( ApkDecoder . java : 103 ) at brut . apktool . Main . cmdDecode ( Main . java : 165 ) at brut . apktool . Main . main ( Main . java : 81 ) Caused by : java . io . EOFException at java . io . DataInputStream . readFully ( DataInputStream . java : 197 ) at com . mindprod . ledatastream . LEDataInputStream . readFully ( LEDataInputStream . java : 201 ) at brut . util . DataInputDelegate . readFully ( DataInputDelegate . java : 69 ) at brut . androlib . res . decoder . StringBlock . read ( StringBlock . java : 66 ) at brut . androlib . res . decoder . AXmlResourceParser . doNext ( AXmlResourceParser . java : 845 ) at brut . androlib . res . decoder . AXmlResourceParser . next ( AXmlResourceParser . java : 96 ) at brut . androlib . res . decoder . AXmlResourceParser . nextToken ( AXmlResourceParser . java : 106 ) at org . xmlpull . v1 . wrapper . classic . XmlPullParserDelegate . nextToken ( XmlPullParserDelegate . java : 105 ) at brut . androlib . res . decoder . XmlPullStreamDecoder . decode ( XmlPullStreamDecoder . java : 140 ) . . . 7 more |
接下來我們将EOFException加入到Breakpoints中。
單擊Breakpoints工具欄上的”Add Java Exception Breakpoint”圖示,搜尋EOFException并添加。
xaingce_005
重新再運作,這時第一次斷下來的位置,經分析是有異常處理的,在此不做贅述。 繼續(F8),當再次斷下來,檢視調用棧和變量:
xaingce_006
我們可以從上至下逐漸檢視調用,當檢視到StringBlock.read,注意到block.m_strings的size是134217564,一個非常大的值。
然後再看size是如何得到的(StringBlock.java的64行):
1 2 | int size = ( ( stylesOffset == 0 ) ? chunkSize : stylesOffset ) - stringsOffset ; |
那麼,stylesOffset的值是134217728,換成16進制為0x80000000,這明顯已經超出了檔案的總長度。 這樣,也就定位到問題的原因了。
3.5 第二次重打包
之前已經解開過apk檔案了,是以隻要将錯誤的地方修改後打包即可。
關健是修改的偏移和數值,這涉及到axml檔案格式的說明,屬于另外文章要講述的内容。
在此推薦一個010Editor模闆,應用模闆後就很容易定位這個位置了。
模闆下載下傳: https://github.com/tomken/010_template_for_android
實際上該StringBlock中并不包含styleString,因為styleCount的值為0。 是以,隻要将styleOffset修改為0就正确了。
具體來說,就是将0x23位置的”08″改為”00″。
xaingce_007
-
build
$ java -jar apktool-cli b -f -o /tmp/xaingce_ok.apk /tmp/xaingce.apk
3.6 驗證結果
經過以上的修改,使用Jeb打開xaingce_ok.apk,此時已能夠正确檢視Manifest和Resources了。
xaingce_010
4 樣本行為分析
最後,我們還是來關心一下這個樣本有什麼惡意行為,那麼還涉及到一些額外要做的工作。
4.1 提取樣本中包含的apk
這個樣本的assets中包含另一個apk,樣本運作後會自動提取并安裝。
我們可以從之前解包的目錄中擷取這個名為com.android.Trogoogle.apk的檔案。
這個apk同樣采用了之前所講的反靜态分析方法,對應修改後就能夠正常使用Jeb分析了。
這個樣本人工分析起來并不算複雜,不過,這次我們使用”翠鳥“來動态分析。
因為初始樣本會令androgurad異常,使得”翠鳥”的分析流程因為無法得到一些關健資料而中斷,修改了檔案格式後就沒有這個問題了。
但是,由于使用Apktool重打包的檔案數字簽名不正确,将無法安裝,是以還需要重新簽名。
4.2 簽名
詳細說明請參看: http://developer.android.com/tools/publishing/app-signing.html
我們隻要簽debug模式的簽名就可以了:
1 2 3 4 5 | $ jarsigner - verbose - sigalg SHA1withRSA - digestalg SHA1 - keystore ~ / . android / debug . keystore \ com . android . Trogoogle . apk androiddebugkey Enter Passphrase for keystore : & lt ; === Passphrase為 "android" |
4.3 惡意行為
在文章開始,其實就已經揭示了該樣本的惡意行為,主要是竊取短信和通訊錄。那麼從”翠鳥”的分析報告中也清楚的看到這些惡意行為。
xaingce_008
另外我們還注意到,該樣本請求解析”smtp.21cn.com”這個域名:
xaingce_009
然後我們從網絡資料的記錄中果然查了樣本上傳隐私資料的郵箱賬号及密碼。
登入後:
xaingce_001
5 小結
惡意樣本通過修改二進制格式檔案中的一些offset或size等數值,來破壞分析程式的解析,但又不影響其在真實系統中運作,以此來逃避自動分析。
當然,反之也可以通過對應的格式解析和校驗,來檢測這類惡意樣本。