基礎知識
這里首先說一下程序自刪除實現的思路:程序創建一個批處理文件,并創建進程執行,然后程序結束進程;批處理所做的功能便是延時5秒后,刪除指定程序然后再自刪除。這樣,程序自刪除功能便實現了。
自刪除的實現主要可以使用兩種方法,一種是利用批處理技術,另外一種則是調用windows提供的api進行實現
首先說一下批處理技術。批處理(Batch),也稱為批處理腳本。顧名思義,批處理就是對某對象進行批量的處理,通常被認為是一種簡化的腳本語言,它應用于DOS和Windows系統中。批處理文件的擴展名為bat 。比較常見的批處理包含兩類:DOS批處理和PS批處理。PS批處理是基于微軟的強大的PowerShell的,用來批量處理一些任務的腳本;而DOS批處理則是基于DOS命令的,用來自動地批量地執行DOS命令以實現特定操作的腳本。更復雜的情況,需要使用if、for、goto等命令控制程式的運行過程,如同C、Basic等高級語言一樣。如果需要實現更復雜的應用,利用外部程式是必要的,這包括系統本身提供的外部命令和第三方提供的工具或者軟件。批處理程序雖然是在命令行環境中運行,但不僅僅能使用命令行軟件,任何當前系統下可運行的程序都可以放在批處理文件中運行。
有些人認為批處理語言的含義要比上面的描述更廣泛,還包括許多軟件自帶的批處理語言,如 Microsoft office、Visual Studio、Adobe Photoshop 所內置的批處理語言的功能,用戶可通過它們讓相應的軟件執行自動化操作(例如調整某個資料夾所有 PSD 圖檔的解析度)。 而這類批處理語言也大多提供把一系列操作錄制為批處理文件的功能,這樣用戶不必寫程式就能得到批處理程序。
在這個地方其實批處理也是一種特殊的語言,比如說我們要在cmd里面執行一些命令,就可以把他寫成一個bat文件。這里能夠使用批處理實現自刪除有一個前提就是,批處理提供了自己刪除自己的命令,如下所示
del %0
在批處理文件執行這個命令之后會直接對文件進行刪除,而不是放入回收站,那么我們就可以先執行我們想要執行的程序,然后在sleep過后使用del %0刪除自身即可。
實現過程
批處理方式
這里有一個注意的點,一種是使用 choice 命令進行延遲,另一種則使用 ping 命令進行延遲。要注意的是,choice 這個命令是從 Windows 2003開始才有這個命令。也就是說,Windows 2003版本或者以上版本才支持這個命令,對于低于Windows 2003的版本是不支持的。Windows XP 版本比Windows 2003版本低,所以不支持 choice 命令。
那么我們首先進行choice命令的實現,bat的代碼如下
@echo off
choice /t 10 /d y /n >nul
del *.exe
del %0
我們整理下思路,要想實現自刪除首先需要知道程序所在的目錄,然后生成批處理文件并生成進程來執行批處理文件,主要用到的是GetModuleFileName這個api
GetModuleFileName
檢索包含指定模塊的文件的完全限定路徑。
Dword GetModuleFileNameA(
[in, optional] HMODULE hModule,
[out] LPSTR lpFilename,
[in] DWORD nSize
);
那么我們首先要寫一個函數進行批處理文件的自動生成,這里直接用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);
然后調用cmd命令行
::wsprintf(szCmd, "cmd /c call "%s"", szBat);
再調用之前編寫的CreateBat創建批處理文件
bRet = CreateBat(szBat);
最后就是使用CreateProcess創建進程,但是這里有一個比較特殊的地方,就是我們需要隱蔽執行,那么我們就可以使用不顯示執行程序窗口的模式,這個參數在CreateProcess的第九個參數,首先看一下CreateProcess的結構
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這個參數,這個參數決定了新進程的主窗體如何顯示的STARTUPINFO結構體,我們繼續跟到STARTUPINFO結構體里面
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的值需要設置為STARTF_USESHOWWINDOW,wShowWindow的值設置為false即可
STARTUPINFO si = { 0 };
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = FALSE;
然后調用CreateProcess啟動進程
BOOL bRet = CreateProcess(NULL, szCmd, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
這里編譯一下看看效果,這里直接退出了,什么也沒有,證明是對的,因為我們隱藏了cmd的窗口
這里我們把wShowWindow的值改為TRUE再查看一下效果
這里看起來效果還是不明顯,我們再換種方式,直接運行exe,發現在同目錄下生成了test.bat
10s過后發現exe跟bat都已經刪除,證明我們的自刪除成功
上面提到,在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,這里我們實現的是cmd.exe的自啟動與刪除,那么在實戰過程中能否寫成cs的上線exe的自刪除呢?答案是肯定的,這里就不拓展了。
MoveFileEx方式
我們首先看一下MoveFileEx這個api
BOOL MoveFileExA(
[in] LPCSTR lpExistingFileName,
[in, optional] LPCSTR lpNewFileName,
[in] DWORD dwFlags
);
dwFlags:設置移動標志,指明要怎樣操作文件或者目錄。
MOVEFILE_COPY_ALLOWED:當需要移動文件到不同的盤符時需要指定此值,不然會失敗,這個值不能和MOVEFILE_DELAY_UNTIL_REBOOT一起用
MOVEFILE_DELAY_UNTIL_REBOOT:文件并不立即移動,當下一次機器重啟時文件才執行移動 ,不能和MOVEFILE_COPY_ALLOWED同時用
MOVEFILE_FAIL_IF_NOT_TRACKABLE:當源文件是連接資源時會移動失敗。
MOVEFILE_REPLACE_EXISTING:當目的文件已經存在時,要將lpExistingFileName的內容替換掉以前的內容,此時要檢查ACL權限,可能會失敗
MOVEFILE_WRITE_THROUGH:只有當文件完全到達目的文件的時候函數才返回,緩沖區也不能有未留的數據
MoveFileEx這個函數調用的時候有幾個需要的點,第一個就是當dwFlags為MOVEFILE_DELAY_UNTIL_REBOOT時,需要為system或administrartor權限才能執行,第二個點就是如果要移動目錄需要保證目錄不存在才可以,第三個點就是不能在不同的盤符下移動目錄。
那么我們這里實現自刪除的話,就是好需要設置dwFlags為MOVEFILE_DELAY_UNTIL_REBOOT,這里為什么要system或者administrator權限呢,是因為MoveFileEx是通過寫入HKEY_LOCAL_macHINESYSTEMCurrentControlSetControlSession ManagerPendingFileRenameOperations這個注冊表路徑來達到移動或刪除的目的,我們可以看到這個鍵是位于HKEY_LOCAL_MACHINE的,而不是USER,所以必須要administrator權限進行修改
這里我們看一下這個鍵值,它的類型是REG_MULTI_SZ,那么意味著這個鍵值能夠寫入多個字符串
經過探究后發現,MoveFileEx這個api在執行刪除操作寫入File到PendingFileRenameOperations,而如果是執行移動操作則是把FileOtherFile寫入PendingFileRenameOperations
那么如何用MoveFileEx實現自刪除呢,首先提兩個概念,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其實是用來驗證文件系統的邏輯完整性的,那么再說說頁面文件:
頁面文件,是指操作系統反映構建并使用虛擬內存的硬盤空間大小而創建的文件。要整理頁面文件,首先將頁面文件從原先所在的驅動器移動到其他驅動器,然后對原來驅動器進行整理,最后再將頁面文件移回到原驅動器上,此時頁面文件就會存放在連續的磁盤空間中了。具體來說,在 windows操作系統下(Windows 2000/XP)pagefile.sys這個文件,它就是系統頁面文件(也就是大家熟知的虛擬內存文件),它的大小取決于打開的程序多少和你原先設置頁面文件的最小最大值,是不斷變化的,有時可能只有幾十MB,有時則達到幾百甚至上千MB。
那么這兩個概念有什么關聯呢,有一個時間節點就是,用戶在啟動計算機時,執行了AUTOCHK,但是還沒有創建頁面文件,在這個時間節點下,可以說話用戶是還沒有完全進入操作系統的,那么這時候就可以刪除在正常情況下刪除不了的文件,我的理解是在沒有創建頁面文件的時候,其實操作系統是還沒有啟動完全的,所以這時候可執行文件其實是沒有完全加載好的。
那么我們知道了原理,這里實現一下,其實代碼相比于批處理方式少了很多,但是涉及到的知識點卻是一點都不少。我們在前面發現在PendingFileRenameOperations鍵的數值數據中,路徑前面都有??,但是這里并不是加上??,在MoveFileEx的函數定義中刪除文件的路徑開頭需要加上\?
所以我們在緩沖區前面先加上\?
char szTemp[MAX_PATH] = "\\?\";
因為我們要把路徑寫在緩沖區后面,就要使用到lstrcat
::lstrcat(szTemp, szFileName);
然后調用MoveFileEx實現自刪除
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;
}
這里我們直接執行一下,發現報錯5,對應GetLastError的報錯屬性是權限不夠,這里我們之前提到過需要修改注冊表,所以直接用user權限啟動是拒絕訪問的
這里我們改用administrator啟動程序,可以看到已經執行成功
到PendingFileRenameOperations鍵值下查看已經添加成功,這里重啟之后就會進行刪除
后記
我們對兩種自刪除的方式進行了實現,這個地方我們可以發現,MoveFileEx方式是需要重啟電腦后才能夠進行刪除,而批處理則可以不用重啟就可以刪除,這里可以根據具體用途才用具體方法進行實現。
本文由Drunkmars原創發布
轉載,請參考轉載聲明,注明出處: https://www.anquanke.com/post/id/259050
安全客 - 有思想的安全新媒體






