背景
隨著移動應用的廣泛普及,惡意軟件也日趨複雜和隱蔽。本報告聚焦於一個由ReBensk 提交至incinerator.cloud 的惡意Android 軟件樣本。
基本信息
樣本哈希值:
MD5: 2f371969faf2dc239206e81d00c579ff
SHA-256: b3561bf581721c84fd92501e2d0886b284e8fa8e7dc193e41ab300a063dfe5f3
經由ReBensk 提交至incinerator.cloud 的多個惡意樣本中,我們特別關注了一款經過自定義修改的APK 文件,以下簡稱為“樣本b356”。這一樣本具有獨特的混淆和隱蔽技術,導致標準的解壓縮工具成功解壓其內容。我們通過針對性修復後,成功解壓縮。但是常用的apk 分析工具仍然分析失敗,通過進一步深度分析,我們得以突破樣本的限制,最終能夠成功分析。
分析過程
1. AndroidManifest.xml 的文件類型修改
1.1 分析失敗的原因
我們利用010 Editor 打開樣本b356 中修復後的AndroidManifest.xml 二進製文件。嘗試用該工具的AndroidResource 模板進行分析時,首次嘗試遭到失敗。從錯誤日誌中了解到,問題起始於文件的pos:0。
通過與正常的AndroidManifest.xml 二進製文件對比,我們發現其首個字節即有差異。
資源文件的類型定義如下:
enum {
RES_NULL_TYPE=0x0000,
RES_STRING_POOL_TYPE=0x0001,
RES_TABLE_TYPE=0x0002,
RES_XML_TYPE=0x0003, //Chunk types in
RES_XML_TYPE RES_XML_FIRST_CHUNK_TYPE=0x0100,
RES_XML_START_NAMESPACE_TYPE=0x0100,
RES_XML_END_NAMESPACE_TYPE=0x0101,
RES_XML_START_ELEMENT_TYPE=0x0102,
RES_XML_END_ELEMENT_TYPE=0x0103,
RES_XML_CDATA_TYPE=0x0104,
RES_XML_LAST_CHUNK_TYPE=0x017f, //This contains a uint32_t array mapping RES_XML_RESOURCE_MAP_TYPE=0x0180, //Chunk types in RES_TABLE_TYPE RES_TABLE_PACKAGE_TYPE=0x0200,
RES_TABLE_TYPE_TYPE=0x0201, RES_TABLE_TYPE_SPEC_TYPE=0x0202
};
按照Android 規範,該字節通常應為0x03,但在樣本b356 中並非如此。
1.2 為什麼 android 系統可以解析
定位到android 系統解析源碼,
這裡是開始解析Androidmanifest.xml 二進製文件的地方,如源碼所示,沒有驗證前兩個字節是否是0x0003,只對headerSize 的合法性做了驗證。所以樣本b356 修改了文件類型後,android 系統仍然能夠正確解析。
1.3 修復建議
靜態分析工具修復建議:和Android 系統解析流程保持一致,此處不校驗文件類型。
2. stringPoolSize 修改
2.1 數據異常點分析
我們手動修正首個字節為0x03 以便進一步分析。再次嘗試用AndroidResource 模板解析後,發現分析仍然失敗,只展示了字符串池(string pool)而沒有解析出XML 主節點。
展開stringoffsets的數據進行查看,從第131 個stringoffset開始,數據顯著異常,遠超文件全長,明顯是錯誤的。進一步分析stringPool的頭部信息,計算出實際的字符串個數為131。
在樣本b356 的stringoffsets字段中,從第131 個開始,數據明顯存在異常:這些值遠遠超過了整個文件的實際長度。這種不一致性明顯指向了一個錯誤,也意味著記錄在stringoffsets中的數量很可能是不准確的。
在深入分析樣本b356 中的stringpool結構時,我們首先確認了strdata[0],即stringpool中的第一個字符串,被正確解析。這一點是非常關鍵的,因為它證明了我們的解析起始位置(0x230,即十進制的560)是準確的。
stringsStart字段指出了從文件頭(header)開始計算,552 個字節後就是stringpool的開始位置。由於通常會有8 個字節用於存儲stringpool的元信息(例如長度或其他標記),所以552+8 恰好等於560。
這自然引發了一個問題:這552 個字節的具體內容是什麼?我們知道strPoolheader自身就佔用了28 個字節(或者說是0x1C)。如果從552 個字節中扣除這28 個字節,那麼剩下的524 個字節用於什麼呢?
剩下的524 個字節非常可能是用於存儲stringoffsets的。每個stringoffset通常會佔用4 個字節,因此524/4 正好等於131。這一點與我們之前通過手動計算得出的stringoffsets數量是一致的。
在之前的深度分析中,我們推斷出stringoffsets的實際數量為131,這與樣本b356 中錯誤地標識的數量不一致。為了能更準確地解析該樣本,我們決定修正stringCount字段。
2.2 為什麼 Android 系統能夠正確解析
如上圖系統解析stringPoolsize的源碼所示,stringPoolsize是計算得到,所以樣本b356 修改了stringPoolsize讓眾多通過從文件中讀取stringPoolsize的靜態分析工具失效,但android 系統在解析時任然可以通過計算得到正確的stringPoolSize。
2.3 修改建議
靜態分析工具修改建議:按照android 系統的做法,stringPoolSize通過計算得到,而不是從文件中解析。
手動調整:首先,在樣本b356 的stringPool頭部中找到stringCount字段,並將其值修改為131。
重新解析:在完成修正操作後,我們再次使用010 Editor配合AndroidResource模板來解析樣本。
經過修改後,我們發現XML 的主要節點已經成功被解析。這意味著我們的手動修正是有效的,並且我們現在能夠看到該樣本的更多內部細節。
儘管我們手動修復了stringCount和成功用010 Editor解析了AndroidManifest.xml的主要節點,使用專用的靜態分析工具依然會出錯。具體的錯誤出現在二進製文件的0x1588字節位置。
3. 在 xml 節點結束位置插入混淆數據
3.1 數據異常分析
我們手動修改stringCount的131,以便繼續分析。
010 Editor 重新加載修改後的文件,結果如圖如下圖所示:
010 Editor 在解析時已經沒有問題了,但是用靜態分析工具解析時在0x1588遇到非預期數據。
在0x1587字節位置,第一個XML 元素已經結束。靜態分析工具預期0x1588字節作為下一個ResChunk_header的起始點進行分析。然而,在0x158A字節位置嘗試解析size字段時,出現異常。根據ResChunk_header結構的約束,這個size值應該至少大於8,但實際數據並沒有滿足這一條件。
樣本b356 應該是在0x1588字節處插入了一些非標准或混淆的數據,導致靜態分析工具分析出錯。
查看010 Editor 的解析結果得知,startEle 的headersize字段進行了改動,以便在元素結束後添加混淆內容。
在解析attribute字段之後,如果解析尚未完成或遇到異常,工具應自動跳過到elementStart + size的位置,從那裡開始解析下一個元素。這樣做的目的是繞過可能存在的干擾或錯誤數據。
3.2 為什麼 Android 系統可以解析
如下圖android 系統源碼所示,讀取每個attribute 的數據通過attributeExt的位置,attributeExt的start,attributeExt的attributeSize 計算得到的,attributeExt的start,attributeExt的attributeSize 從byte 中讀取,所以只要attributeExt的attributeStart 正確,就能保證獲取到正確attributeData。
那attributeExt是怎麼定位的?如下圖源碼所示,每次解析下一個節點的時候,都是當前節點的位置加上header 中讀到的size的長度。
以上文中的案例來描述就是,startEle[1]的位置=startEle[0]的位置+startEle[0].header-size。所以樣本b356 在原先apk 的startEle[0]結束後填充干擾數據的同時,也修改了startEle[0].header-size 的長度,從而讓系統正確解析。
3.3 修改建議
靜態分析工具修改建議:參照Android 系統的解析方法,每個xml 節點的起始位置是通過上一個節點的起始位置+ 上一個節點的長度。
總結
“b356”樣本展示了多層次、多維度的混淆和隱蔽技術,其目的明確:抵制和破壞標準解壓縮工具和靜態分析解碼的能力。
b356 能夠混淆成功,主要原因是android 系統解析處理流程和市面上常用的靜態分析工具在一些細節上存在出入。
此樣本中只有三個修改點,其他的樣本中還存在一下其他的點,我們在下一篇blog 中會接著分析。
但是主動的對抗方式肯定不是針對每個點修修補補,而是統一靜態分析方式和android 系統解析流程。比方說,把系統源碼中解析方式轉換成靜態分析工具的方式,這個需要社區的共同努力。
來源:https://www.liansecurity.com/#/main/news/IPONQIoBE2npFSfFbCRf/detail
Recommended Comments