基礎(chǔ)知識
這里首先說一下程序自刪除實現(xiàn)的思路:程序創(chuàng)建一個批處理文件,并創(chuàng)建進程執(zhí)行,然后程序結(jié)束進程;批處理所做的功能便是延時5秒后,刪除指定程序然后再自刪除。這樣,程序自刪除功能便實現(xiàn)了。
自刪除的實現(xiàn)主要可以使用兩種方法,一種是利用批處理技術(shù),另外一種則是調(diào)用windows提供的api進行實現(xiàn)
首先說一下批處理技術(shù)。批處理(Batch),也稱為批處理腳本。顧名思義,批處理就是對某對象進行批量的處理,通常被認為是一種簡化的腳本語言,它應用于DOS和Windows系統(tǒng)中。批處理文件的擴展名為bat 。比較常見的批處理包含兩類:DOS批處理和PS批處理。PS批處理是基于微軟的強大的PowerShell的,用來批量處理一些任務(wù)的腳本;而DOS批處理則是基于DOS命令的,用來自動地批量地執(zhí)行DOS命令以實現(xiàn)特定操作的腳本。更復雜的情況,需要使用if、for、goto等命令控制程式的運行過程,如同C、Basic等高級語言一樣。如果需要實現(xiàn)更復雜的應用,利用外部程式是必要的,這包括系統(tǒng)本身提供的外部命令和第三方提供的工具或者軟件。批處理程序雖然是在命令行環(huán)境中運行,但不僅僅能使用命令行軟件,任何當前系統(tǒng)下可運行的程序都可以放在批處理文件中運行。
有些人認為批處理語言的含義要比上面的描述更廣泛,還包括許多軟件自帶的批處理語言,如 Microsoft office、Visual Studio、Adobe Photoshop 所內(nèi)置的批處理語言的功能,用戶可通過它們讓相應的軟件執(zhí)行自動化操作(例如調(diào)整某個資料夾所有 PSD 圖檔的解析度)。 而這類批處理語言也大多提供把一系列操作錄制為批處理文件的功能,這樣用戶不必寫程式就能得到批處理程序。
在這個地方其實批處理也是一種特殊的語言,比如說我們要在cmd里面執(zhí)行一些命令,就可以把他寫成一個bat文件。這里能夠使用批處理實現(xiàn)自刪除有一個前提就是,批處理提供了自己刪除自己的命令,如下所示
del %0
在批處理文件執(zhí)行這個命令之后會直接對文件進行刪除,而不是放入回收站,那么我們就可以先執(zhí)行我們想要執(zhí)行的程序,然后在sleep過后使用del %0刪除自身即可。
實現(xiàn)過程
批處理方式
這里有一個注意的點,一種是使用 choice 命令進行延遲,另一種則使用 ping 命令進行延遲。要注意的是,choice 這個命令是從 Windows 2003開始才有這個命令。也就是說,Windows 2003版本或者以上版本才支持這個命令,對于低于Windows 2003的版本是不支持的。Windows XP 版本比Windows 2003版本低,所以不支持 choice 命令。
那么我們首先進行choice命令的實現(xiàn),bat的代碼如下
@echo off
choice /t 10 /d y /n >nul
del *.exe
del %0
我們整理下思路,要想實現(xiàn)自刪除首先需要知道程序所在的目錄,然后生成批處理文件并生成進程來執(zhí)行批處理文件,主要用到的是GetModuleFileName這個api
GetModuleFileName
檢索包含指定模塊的文件的完全限定路徑。
Dword GetModuleFileNameA(
[in, optional] HMODULE hModule,
[out] LPSTR lpFilename,
[in] DWORD nSize
);
那么我們首先要寫一個函數(shù)進行批處理文件的自動生成,這里直接用wsprintf寫入即可
::wsprintf(szBat, "@echo offnchoice /t %d /d y /n >nulndel *.exendel %%0n", time);
然后使用fopen_s、fwrite生成批處理文件
FILE *fp = NULL;
fopen_s(&fp, pszBatName, "w+");
fwrite(szBat, (1 + ::lstrlen(szBat)), 1, fp);
完整代碼如下
BOOL CreateBat(char *pszBatFileName)
{
int time = 5;
char szBat[MAX_PATH] = { 0 };
::wsprintf(szBat, "@echo offnchoice /t %d /d y /n >nulndel *.exendel %%0n", time);
FILE *fp = NULL;
fopen_s(&fp, pszBatFileName, "w+");
if (NULL == fp)
{
return FALSE;
}
fwrite(szBat, (1 + ::lstrlen(szBat)), 1, fp);
fclose(fp);
return TRUE;
}
然后我們首先獲取程序所在的目錄
::GetModuleFileName(NULL, szPath, MAX_PATH);
然后把批處理文件跟程序放到同一目錄下
::wsprintf(szBat, "%s\test.bat", szPath);
然后調(diào)用cmd命令行
::wsprintf(szCmd, "cmd /c call "%s"", szBat);
再調(diào)用之前編寫的CreateBat創(chuàng)建批處理文件
bRet = CreateBat(szBat);
最后就是使用CreateProcess創(chuàng)建進程,但是這里有一個比較特殊的地方,就是我們需要隱蔽執(zhí)行,那么我們就可以使用不顯示執(zhí)行程序窗口的模式,這個參數(shù)在CreateProcess的第九個參數(shù),首先看一下CreateProcess的結(jié)構(gòu)
BOOL CreateProcessA(
[in, optional] LPCSTR lpApplicationName,
[in, out, optional] LPSTR lpCommandLine,
[in, optional] LPSECURITY_ATTRIBUTES lpProcessAttributes,
[in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] BOOL bInheritHandles,
[in] DWORD dwCreationFlags,
[in, optional] LPVOID lpEnvironment,
[in, optional] LPCSTR lpCurrentDirectory,
[in] LPSTARTUPINFOA lpStartupInfo,
[out] LPPROCESS_INFORMATION lpProcessInformation
);
就是LPSTARTUPINFOA這個參數(shù),這個參數(shù)決定了新進程的主窗體如何顯示的STARTUPINFO結(jié)構(gòu)體,我們繼續(xù)跟到STARTUPINFO結(jié)構(gòu)體里面
typedef struct _STARTUPINFOA {
DWORD cb;
LPSTR lpReserved;
LPSTR lpDesktop;
LPSTR lpTitle;
DWORD dwX;
DWORD dwY;
DWORD dwXSize;
DWORD dwYSize;
DWORD dwXCountChars;
DWORD dwYCountChars;
DWORD dwFillAttribute;
DWORD dwFlags;
WORD wShowWindow;
WORD cbReserved2;
LPBYTE lpReserved2;
HANDLE hStdInput;
HANDLE hStdOutput;
HANDLE hStdError;
} STARTUPINFOA, *LPSTARTUPINFOA;
若要隱藏窗口,則dwFlags的值需要設(shè)置為STARTF_USESHOWWINDOW,wShowWindow的值設(shè)置為false即可
STARTUPINFO si = { 0 };
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = FALSE;
然后調(diào)用CreateProcess啟動進程
BOOL bRet = CreateProcess(NULL, szCmd, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
這里編譯一下看看效果,這里直接退出了,什么也沒有,證明是對的,因為我們隱藏了cmd的窗口
這里我們把wShowWindow的值改為TRUE再查看一下效果
這里看起來效果還是不明顯,我們再換種方式,直接運行exe,發(fā)現(xiàn)在同目錄下生成了test.bat
10s過后發(fā)現(xiàn)exe跟bat都已經(jīng)刪除,證明我們的自刪除成功
上面提到,在xp是沒有choice的命令的,那么可以用ping命令代替,bat的代碼如下
@echo off
ping 127.0.0.1 -n 10
del *.exe
del %0
與choice相似,這里就不細說了,直接改一下代碼就可以
BOOL CreateBat(char *pszBatFileName)
{
int time = 5;
char szBat[MAX_PATH] = { 0 };
::wsprintf(szBat, "@echo offnping 127.0.0.1 -n %dndel *.exendel %%0n", time);
FILE *fp = NULL;
fopen_s(&fp, pszBatFileName, "w+");
if (NULL == fp)
{
return FALSE;
}
fwrite(szBat, (1 + ::lstrlen(szBat)), 1, fp);
fclose(fp);
return TRUE;
}
這里再提一個小tips,這里我們實現(xiàn)的是cmd.exe的自啟動與刪除,那么在實戰(zhàn)過程中能否寫成cs的上線exe的自刪除呢?答案是肯定的,這里就不拓展了。
MoveFileEx方式
我們首先看一下MoveFileEx這個api
BOOL MoveFileExA(
[in] LPCSTR lpExistingFileName,
[in, optional] LPCSTR lpNewFileName,
[in] DWORD dwFlags
);
dwFlags:設(shè)置移動標志,指明要怎樣操作文件或者目錄。
MOVEFILE_COPY_ALLOWED:當需要移動文件到不同的盤符時需要指定此值,不然會失敗,這個值不能和MOVEFILE_DELAY_UNTIL_REBOOT一起用
MOVEFILE_DELAY_UNTIL_REBOOT:文件并不立即移動,當下一次機器重啟時文件才執(zhí)行移動 ,不能和MOVEFILE_COPY_ALLOWED同時用
MOVEFILE_FAIL_IF_NOT_TRACKABLE:當源文件是連接資源時會移動失敗。
MOVEFILE_REPLACE_EXISTING:當目的文件已經(jīng)存在時,要將lpExistingFileName的內(nèi)容替換掉以前的內(nèi)容,此時要檢查ACL權(quán)限,可能會失敗
MOVEFILE_WRITE_THROUGH:只有當文件完全到達目的文件的時候函數(shù)才返回,緩沖區(qū)也不能有未留的數(shù)據(jù)
MoveFileEx這個函數(shù)調(diào)用的時候有幾個需要的點,第一個就是當dwFlags為MOVEFILE_DELAY_UNTIL_REBOOT時,需要為system或administrartor權(quán)限才能執(zhí)行,第二個點就是如果要移動目錄需要保證目錄不存在才可以,第三個點就是不能在不同的盤符下移動目錄。
那么我們這里實現(xiàn)自刪除的話,就是好需要設(shè)置dwFlags為MOVEFILE_DELAY_UNTIL_REBOOT,這里為什么要system或者administrator權(quán)限呢,是因為MoveFileEx是通過寫入HKEY_LOCAL_macHINESYSTEMCurrentControlSetControlSession ManagerPendingFileRenameOperations這個注冊表路徑來達到移動或刪除的目的,我們可以看到這個鍵是位于HKEY_LOCAL_MACHINE的,而不是USER,所以必須要administrator權(quán)限進行修改
這里我們看一下這個鍵值,它的類型是REG_MULTI_SZ,那么意味著這個鍵值能夠?qū)懭攵鄠€字符串
經(jīng)過探究后發(fā)現(xiàn),MoveFileEx這個api在執(zhí)行刪除操作寫入File到PendingFileRenameOperations,而如果是執(zhí)行移動操作則是把FileOtherFile寫入PendingFileRenameOperations
那么如何用MoveFileEx實現(xiàn)自刪除呢,首先提兩個概念,AUTOCHK和頁面文件。
這里說下何為AUTOCHK:
在msdn的官方解釋中,AUTOCHK的含義是:Runs when the computer is started and prior to Windows Server starting to verify the logical integrity of a file system.
也就是說AUTOCHK其實是用來驗證文件系統(tǒng)的邏輯完整性的,那么再說說頁面文件:
頁面文件,是指操作系統(tǒng)反映構(gòu)建并使用虛擬內(nèi)存的硬盤空間大小而創(chuàng)建的文件。要整理頁面文件,首先將頁面文件從原先所在的驅(qū)動器移動到其他驅(qū)動器,然后對原來驅(qū)動器進行整理,最后再將頁面文件移回到原驅(qū)動器上,此時頁面文件就會存放在連續(xù)的磁盤空間中了。具體來說,在 windows操作系統(tǒng)下(Windows 2000/XP)pagefile.sys這個文件,它就是系統(tǒng)頁面文件(也就是大家熟知的虛擬內(nèi)存文件),它的大小取決于打開的程序多少和你原先設(shè)置頁面文件的最小最大值,是不斷變化的,有時可能只有幾十MB,有時則達到幾百甚至上千MB。
那么這兩個概念有什么關(guān)聯(lián)呢,有一個時間節(jié)點就是,用戶在啟動計算機時,執(zhí)行了AUTOCHK,但是還沒有創(chuàng)建頁面文件,在這個時間節(jié)點下,可以說話用戶是還沒有完全進入操作系統(tǒng)的,那么這時候就可以刪除在正常情況下刪除不了的文件,我的理解是在沒有創(chuàng)建頁面文件的時候,其實操作系統(tǒng)是還沒有啟動完全的,所以這時候可執(zhí)行文件其實是沒有完全加載好的。
那么我們知道了原理,這里實現(xiàn)一下,其實代碼相比于批處理方式少了很多,但是涉及到的知識點卻是一點都不少。我們在前面發(fā)現(xiàn)在PendingFileRenameOperations鍵的數(shù)值數(shù)據(jù)中,路徑前面都有??,但是這里并不是加上??,在MoveFileEx的函數(shù)定義中刪除文件的路徑開頭需要加上\?
所以我們在緩沖區(qū)前面先加上\?
char szTemp[MAX_PATH] = "\\?\";
因為我們要把路徑寫在緩沖區(qū)后面,就要使用到lstrcat
::lstrcat(szTemp, szFileName);
然后調(diào)用MoveFileEx實現(xiàn)自刪除
BOOL bRet = ::MoveFileEx(szTemp, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
完整代碼如下
BOOL MoveDel(char* szFileName)
{
char szTemp[MAX_PATH] = "\\?\";
::lstrcatA(szTemp, szFileName);
BOOL bRet = ::MoveFileExA(szTemp, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
if (bRet == NULL)
{
printf("[!] MoveFileExA failed, error is : %dnn", GetLastError());
return FALSE;
}
else
{
printf("[*] MoveFileExA successfully!nn");
}
return TRUE;
}
這里我們直接執(zhí)行一下,發(fā)現(xiàn)報錯5,對應GetLastError的報錯屬性是權(quán)限不夠,這里我們之前提到過需要修改注冊表,所以直接用user權(quán)限啟動是拒絕訪問的
這里我們改用administrator啟動程序,可以看到已經(jīng)執(zhí)行成功
到PendingFileRenameOperations鍵值下查看已經(jīng)添加成功,這里重啟之后就會進行刪除
后記
我們對兩種自刪除的方式進行了實現(xiàn),這個地方我們可以發(fā)現(xiàn),MoveFileEx方式是需要重啟電腦后才能夠進行刪除,而批處理則可以不用重啟就可以刪除,這里可以根據(jù)具體用途才用具體方法進行實現(xiàn)。
本文由Drunkmars原創(chuàng)發(fā)布
轉(zhuǎn)載,請參考轉(zhuǎn)載聲明,注明出處: https://www.anquanke.com/post/id/259050
安全客 - 有思想的安全新媒體