C/C++惡意代碼盤點(二):后門丨文件監(jiān)控丨自刪除功能
惡意代碼的分類包括計算機病毒、蠕蟲、木馬、后門、Rootkit、流氓軟件、間諜軟件、廣告軟件、僵尸(bot) 、Exploit等等,有些技術經常用到,有的也是必然用到。

昨天咱們分享了一部分,那么今天我們就分享其他一些技術,主要包括:后門、文件監(jiān)控、文件自動刪除等。
?后門
后門常以套件的形式存在,用于將受害者信息發(fā)送給攻擊者或者傳輸惡意可執(zhí)行程序(下載器),最常用的功能是接收攻擊端傳送過來的命令,執(zhí)行某些操作。
Windows系統(tǒng)中有很多WIN32 API可以執(zhí)行CMD命令,例如system Winexe CreateProcess等。這里介紹通過匿名管道實現(xiàn)遠程CMD。
具體過程
1、初始化匿名管道的SECURITY_ATTRIBUTES結構體,調用CreatePipe創(chuàng)建匿名管道,獲取管道數(shù)據讀取句柄和寫入句柄。
2、初始化STARTUPINFO結構體,隱藏進程窗口,并把管道數(shù)據寫入句柄賦值給新進程控制臺窗口的緩存句柄。
3、調用CreateProcess函數(shù)創(chuàng)建進程,執(zhí)行CMD命令并調用WaitForSingleObject等待命令執(zhí)行完。
4、調用ReadFile根據匿名管道的數(shù)據讀取句柄從匿名管道的緩沖區(qū)中讀取數(shù)據。
5、關閉句柄,釋放資源。
源代碼實現(xiàn):
#include?"stdafx.h"#include "PipeCmd.h"void ShowError(char *pszText){char szErr[MAX_PATH] = {0};::wsprintf(szErr, "%s Error[%d]\n", pszText, ::GetLastError());::MessageBox(NULL, szErr, "ERROR", MB_OK);}// 執(zhí)行 cmd 命令, 并獲取執(zhí)行結果數(shù)據BOOL PipeCmd(char *pszCmd, char *pszResultBuffer, DWORD dwResultBufferSize){HANDLE hReadPipe = NULL;HANDLE hWritePipe = NULL;SECURITY_ATTRIBUTES securityAttributes = {0};BOOL bRet = FALSE;STARTUPINFO si = {0};PROCESS_INFORMATION pi = {0};// 設定管道的安全屬性securityAttributes.bInheritHandle = TRUE;securityAttributes.nLength = sizeof(securityAttributes);securityAttributes.lpSecurityDescriptor = NULL;// 創(chuàng)建匿名管道bRet = ::CreatePipe(&hReadPipe, &hWritePipe, &securityAttributes, 0);if (FALSE == bRet){ShowError("CreatePipe");return FALSE;}// 設置新進程參數(shù)si.cb = sizeof(si);si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;si.wShowWindow = SW_HIDE;si.hStdError = hWritePipe;si.hStdOutput = hWritePipe;// 創(chuàng)建新進程執(zhí)行命令, 將執(zhí)行結果寫入匿名管道中bRet = ::CreateProcess(NULL, pszCmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);if (FALSE == bRet){ShowError("CreateProcess");}// 等待命令執(zhí)行結束::WaitForSingleObject(pi.hThread, INFINITE);::WaitForSingleObject(pi.hProcess, INFINITE);// 從匿名管道中讀取結果到輸出緩沖區(qū)::RtlZeroMemory(pszResultBuffer, dwResultBufferSize);::ReadFile(hReadPipe, pszResultBuffer, dwResultBufferSize, NULL, NULL);// 關閉句柄, 釋放內存::CloseHandle(pi.hThread);::CloseHandle(pi.hProcess);::CloseHandle(hWritePipe);::CloseHandle(hReadPipe);return TRUE;}
文件監(jiān)控
全局鉤子可以實現(xiàn)系統(tǒng)監(jiān)控,Windows提供了一個文件監(jiān)控接口函數(shù)ReadDirectoryChangesW該函數(shù)可以對計算機上所有文件操作進行監(jiān)控。在調用。
ReadDirectoryChangesW設置監(jiān)控過濾條件之前,需要通過CreateFile函數(shù)打開監(jiān)控目錄,獲取監(jiān)控目錄的句柄,之后才能調用ReadDirectoryChangesW函數(shù)設置監(jiān)控過濾條件并阻塞,直到有滿足監(jiān)控過濾條件的操作,ReadDirectoryChangesW才會返回監(jiān)控數(shù)據繼續(xù)往下執(zhí)行。
具體過程
1、打開目錄,獲取文件句柄,調用CreateFile獲取文件句柄,文件句柄必須要有FILE_LIST_DIRECTORY權限。
2、調用ReadDirectoryChangesW設置目錄監(jiān)控。
3、判斷文件操作類型,只要有滿足過濾條件的文件操作,ReadDirectoryChangesW函數(shù)會立馬返回信息,并將其返回到輸出緩沖區(qū)中,而且返回數(shù)據是按結構體FILE_NOTIFY_INFORMATION返回的。
調用一次ReadDirectoryChangesW函數(shù)只會監(jiān)控一次,要想實現(xiàn)持續(xù)監(jiān)控,則需要程序循環(huán)調用ReadDirectoryChangesW函數(shù)來設置監(jiān)控并獲取監(jiān)控數(shù)據,由于持續(xù)的目錄監(jiān)控需要不停循環(huán)調用ReadDirectoryChangesW函數(shù)進行設置監(jiān)控和獲取監(jiān)控數(shù)據,所以如果把這段代碼放在主線程中則會導致程序阻塞,為了解決主線程阻塞的問題,可以創(chuàng)建一個文件監(jiān)控子線程,把文件監(jiān)控的實現(xiàn)代碼放到子線程中。
源代碼實現(xiàn):
void ShowError(char *pszText){char szErr[MAX_PATH] = { 0 };::wsprintf(szErr, "%s Error[%d]\n", pszText, ::GetLastError());::MessageBox(NULL, szErr, "ERROR", MB_OK | MB_ICONERROR);}// 寬字節(jié)字符串轉多字節(jié)字符串void W2C(wchar_t *pwszSrc, int iSrcLen, char *pszDest, int iDestLen){::RtlZeroMemory(pszDest, iDestLen);// 寬字節(jié)字符串轉多字節(jié)字符串::WideCharToMultiByte(CP_ACP,0,pwszSrc,(iSrcLen / 2),pszDest,iDestLen,NULL,NULL);}// 目錄監(jiān)控多線程UINT MonitorFileThreadProc(LPVOID lpVoid){char *pszDirectory = (char *)lpVoid;// 打開目錄, 獲取文件句柄HANDLE hDirectory = ::CreateFile(pszDirectory, FILE_LIST_DIRECTORY,FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS, NULL);if (INVALID_HANDLE_VALUE == hDirectory){ShowError("CreateFile");return 1;}char szTemp[MAX_PATH] = { 0 };BOOL bRet = FALSE;DWORD dwRet = 0;DWORD dwBufferSize = 2048;// 申請一個足夠大的緩沖區(qū)BYTE *pBuf = new BYTE[dwBufferSize];if (NULL == pBuf){ShowError("new");return 2;}FILE_NOTIFY_INFORMATION *pFileNotifyInfo = (FILE_NOTIFY_INFORMATION *)pBuf;// 開始循環(huán)設置監(jiān)控do{::RtlZeroMemory(pFileNotifyInfo, dwBufferSize);// 設置監(jiān)控目錄bRet = ::ReadDirectoryChangesW(hDirectory,pFileNotifyInfo,dwBufferSize,TRUE,FILE_NOTIFY_CHANGE_FILE_NAME | // 修改文件名FILE_NOTIFY_CHANGE_ATTRIBUTES | // 修改文件屬性FILE_NOTIFY_CHANGE_LAST_WRITE, // 最后一次寫入&dwRet,NULL,NULL);if (FALSE == bRet){ShowError("ReadDirectoryChangesW");break;}// 將寬字符轉換成窄字符W2C((wchar_t *)(&pFileNotifyInfo->FileName), pFileNotifyInfo->FileNameLength, szTemp, MAX_PATH);// 判斷操作類型并顯示switch (pFileNotifyInfo->Action){case FILE_ACTION_ADDED:{// 新增文件printf("[File Added Action]%s\n", szTemp);break;}default:{break;}}} while (bRet);// 關閉句柄, 釋放內存::CloseHandle(hDirectory);delete[] pBuf;pBuf = NULL;return 0;}// 創(chuàng)建目錄監(jiān)控多線程void MonitorFile(char *pszDirectory){// 創(chuàng)建文件監(jiān)控多線程::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MonitorFileThreadProc, pszDirectory, 0, NULL);}
自刪除
自刪除功能對病毒木馬來說同樣至關重要,它通常在完成目標任務之后刪除自身,不留下任何蛛絲馬跡,自刪除的方法有很多種,常見的有利用MoveFileEx重啟刪除和利用批處理刪除兩種方式。
(1)MoveFileEx重啟刪除
MOVEFILE_DELAY_UNTIL_REBOOT這個標志只能由擁有管理員權限的程序或者擁有本地系統(tǒng)權限的程序使用,而且這個標志不能MOVEFILE_COPY_ALLOWED一起使用,并且,刪除文件的路徑開頭需要加上“\?\"前綴。
源代碼實現(xiàn):
BOOL RebootDelete(char *pszFileName){// 重啟刪除文件char szTemp[MAX_PATH] = "\\\\?\\";::lstrcat(szTemp, pszFileName);BOOL bRet = ::MoveFileEx(szTemp, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);return bRet;}int _tmain(int argc, _TCHAR* argv[]){if (FALSE == RebootDelete("C:\\Users\\Test\\Desktop\\520.exe")){printf("Set Reboot Delete Error.\n");}else{printf("Set Reboot Delete OK.\n");}system("pause");return 0;}
(2)利用批處理命令刪除
del %0
批處理命令會將自身批處理文件刪除而且不放進回收站。
具體流程
1 構造自刪除批處理文件,該批處理文件的功能就是先利用choice或ping命令延遲一定的時間,之后才開始執(zhí)行刪除文件操作,最后執(zhí)行自刪除命令。
2 在程序中創(chuàng)建一個新進程并調用批處理文件,程序在進程創(chuàng)建成功后,立刻退出整個程序。
源代碼實現(xiàn):C語言資源匯總
BOOL CreateChoiceBat(char *pszBatFileName){int iTime = 5;char szBat[MAX_PATH] = { 0 };// 構造批處理內容/*@echo offchoice /t 5 /d y /n >nuldel *.exedel %0*/::wsprintf(szBat, "@echo off\nchoice /t %d /d y /n >nul\ndel *.exe\ndel %%0\n", iTime);// 生成批處理文件FILE *fp = NULL;fopen_s(&fp, pszBatFileName, "w+");if (NULL == fp){return FALSE;}fwrite(szBat, (1 + ::lstrlen(szBat)), 1, fp);fclose(fp);return TRUE;}BOOL CreatePingBat(char *pszBatFileName){int iTime = 5;char szBat[MAX_PATH] = {0};// 構造批處理內容/*@echo offping 127.0.0.1 -n 5del *.exedel %0*/::wsprintf(szBat, "@echo off\nping 127.0.0.1 -n %d\ndel *.exe\ndel %%0\n", iTime);// 生成批處理文件FILE *fp = NULL;fopen_s(&fp, pszBatFileName, "w+");if (NULL == fp){return FALSE;}fwrite(szBat, (1 + ::lstrlen(szBat)), 1, fp);fclose(fp);return TRUE;}BOOL DelSelf(int iType){BOOL bRet = FALSE;char szCurrentDirectory[MAX_PATH] = {0};char szBatFileName[MAX_PATH] = {0};char szCmd[MAX_PATH] = {0};// 獲取當前程序所在目錄::GetModuleFileName(NULL, szCurrentDirectory, MAX_PATH);char *p = strrchr(szCurrentDirectory, '\\');p[0] = '\0';// 構造批處理文件路徑::wsprintf(szBatFileName, "%s\\temp.bat", szCurrentDirectory);// 構造調用執(zhí)行批處理的 CMD 命令行::wsprintf(szCmd, "cmd /c call \"%s\"", szBatFileName);// 創(chuàng)建自刪除的批處理文件if (0 == iType){// choice 方式bRet = CreateChoiceBat(szBatFileName);}else if (1 == iType){// ping 方式bRet = CreatePingBat(szBatFileName);}// 創(chuàng)建新的進程, 以隱藏控制臺的方式執(zhí)行批處理if (bRet){STARTUPINFO si = { 0 };PROCESS_INFORMATION pi;si.cb = sizeof(si);//指定wShowWindow成員有效si.dwFlags = STARTF_USESHOWWINDOW;//此成員設為TRUE的話則顯示新建進程的主窗口si.wShowWindow = FALSE;BOOL bRet = CreateProcess(//不在此指定可執(zhí)行文件的文件名NULL,//命令行參數(shù)szCmd,//默認進程安全性NULL,//默認進程安全性NULL,//指定當前進程內句柄不可以被子進程繼承FALSE,//為新進程創(chuàng)建一個新的控制臺窗口CREATE_NEW_CONSOLE,//使用本進程的環(huán)境變量NULL,//使用本進程的驅動器和目錄NULL,&si,&pi);if (bRet){//不使用的句柄最好關掉CloseHandle(pi.hThread);CloseHandle(pi.hProcess);// 結束進程exit(0);::ExitProcess(NULL);}}return bRet;}int _tmain(int argc, _TCHAR* argv[]){// 程序自刪除BOOL bRet = DelSelf( 0 );if (FALSE == bRet){printf("Selft Delete Error!\n");}else{printf("Selft Delete OK!\n");}system("pause");return 0;}
注:惡意代碼的存在不是由于黑客之類的手段,主要還是我們開發(fā)過程中很多情況會用到這樣的技術,所以大家請利用技術做正確的事情!
版權申明:內容來源網絡,版權歸原創(chuàng)者所有。除非無法確認,我們都會標明作者及出處,如有侵權煩請告知,我們會立即刪除并表示歉意。謝謝!
