http://jackaldire.com/201004/exe-self-delete-and-self-modify/
其实真正的删除自己肯定是做不到的,至少用户态不行。windows下只要一个文件被某个进程打开就不能被删掉(Linux下可以删除任何打开的文件,只要有权限,而且一般不会影响程序的执行,因为文件系统会等到所有的打开的fd都释放后会才回收inode和data),所以一个windows进程在运行的时候是肯定不可以删除自己的可执行文件的,只能想一些旁门左道了。
方法其实很简单,就是在程序结束前开另一个进程去删自己,但是要求是自己删自己,所以只能借助系统utils。这时候就需要bat脚本来帮忙了。在CMD下删除一个文件很容易,一条del命令就搞定了,但是需要保证执行脚本的时候原进程已经结束运行了,一般来说用一条延时命令walk around“ping 127.0.0.1 -n 2”,2为秒数,然后用“&”连接del命令删除文件
ping 127.0.0.1 -n 2 >nul && del path/to/your/file.exe
更为保险的做法是在bat里用tasklist命令查看进程,循环直到找不到当前进程位置
原来的和同学讨论的是一个程序如何自己修改自己,做到单文件自动更新,实际上和自己删除自己是一个问题。无非就是先生成一个自身的副本,修改副本,再程序结束后用bat脚本覆盖自己就ok了,(如果该文件很大, 建立副本是不是不现实)源代码如下
/////////////////////////////////////////////////////
#include <windows.h>
#include <stdio.h>
int main(int argc, char * argv[])
{
char self[_MAX_PATH];
char swap[_MAX_PATH];
// get the full path of myself
GetModuleFileName(NULL, self, _MAX_PATH);
// copy myself to a temp file for modify
strcpy(swap, self);
strcat(swap, ".tmp"); // add .tmp postfix. better to use tempnam()
CopyFile(self, swap, FALSE);
FILE * fp = fopen(swap, "ab");
// pending whatever you want to the exe
fwrite("Hack is not good, but i love it.", 32, 1, fp);
fclose(fp);
// "ping" is used for delay to wait the main process terminated.
// "move /Y src dst" : overide whtout warnning
// quotation marks are needed for paths which contain blank.
char bat[2*_MAX_PATH + 30] = "cmd /c ping 127.0.0.1 -n 2 >nul && move /Y /"";
strcat(bat, swap);
strcat(bat, "/"");
strcat(bat, " /"");
strcat(bat, self);
strcat(bat, "/""); // bat += "path/to/swap" "path/to/self"
WinExec(bat, SW_HIDE); // Exec the bat script with window hiden
return 0;
}
用ping命令延时只是一个walk around,在系统负载很重的情况下,2秒程序也未必退出了,所以应该寻找一个方法确定一个exe是否在运行。小研究了一下bat,发现通过tasklist命令和find命令可以搞定:
:loop
tasklist /NH | find /i "xxx.exe"
if %ERRORLEVEL% equ 1 (
goto loop
) else (
del path/to/your/file/xxx.exe
)
exit
ERRORLEVEL变量是上一条命令的返回值,不停的在进程列表里寻找xxx.exe直到找不到为止。
// 通用自删除
网上看了一些自删除的文章, 比较复杂的是使用汇编, 但最重要的是使用汇编的方法不是总是有效。
反而使用命令行的却更有效和简单。
// 基本原理就是利用命令行来删除文件
// C:/WINDOWS/system32/cmd.exe /c del "E:/CODE_T~1/asm/del_self.exe"
// 方法1
void DelSel2()
{
// 采用批处理
SHELLEXECUTEINFO ExeInfo;
TCHAR ExePath[MAX_PATH] = {0};
TCHAR ParamPath[MAX_PATH] = {0};
TCHAR ComposePath[MAX_PATH] = {0};
GetModuleFileName(NULL,ExePath,MAX_PATH);
GetShortPathName(ExePath,ExePath,MAX_PATH);
GetEnvironmentVariable(_T("COMSPEC"),ComposePath,MAX_PATH);
_tcscpy(ParamPath,_T("/c del "));
_tcscat(ParamPath,ExePath);
_tcscat(ParamPath,_T(" > nul"));
ZeroMemory(&ExeInfo,sizeof(ExeInfo));
ExeInfo.cbSize = sizeof(ExeInfo);
ExeInfo.hwnd = 0;
ExeInfo.lpVerb = _T("Open"); //执行动作,打开
ExeInfo.lpFile = ComposePath; //执行文件全路径名称
ExeInfo.lpParameters = ParamPath; //执行参数
ExeInfo.nShow = SW_HIDE; //执行方式,隐藏窗口。
ExeInfo.fMask = SEE_MASK_NOCLOSEPROCESS; //设置为ShellExecute函数结束后进程退出。
//创建执行命令窗口进程
if (ShellExecuteEx(&ExeInfo))
{
//设置命令行进程级别为空闲基本,这使得本程序有足够的时间退出。
SetPriorityClass(ExeInfo.hProcess,IDLE_PRIORITY_CLASS);
//设置本程序进程基本为实时执行,快速退出。
SetPriorityClass(GetCurrentProcess(),REALTIME_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_TIME_CRITICAL);
//通知资源管理器,本程序删除
SHChangeNotify(SHCNE_DELETE,SHCNF_PATH,ExePath,NULL);
}
}
// 方法2
BOOL SelfDelete()
{
TCHAR szFile[MAX_PATH], szCmd[MAX_PATH];
if((GetModuleFileName(0,szFile,MAX_PATH)!=0) && (GetShortPathName(szFile,szFile,MAX_PATH)!=0))
{
lstrcpy(szCmd,"/c del ");
lstrcat(szCmd,szFile);
lstrcat(szCmd," >> NUL");
if((GetEnvironmentVariable("ComSpec",szFile,MAX_PATH)!=0) && ((INT)ShellExecute(0,0,szFile,szCmd,0,SW_HIDE)>32))
return TRUE;
}
return FALSE;
}
// 方法3
WinExec((std::string("cmd /c del ") + _pgmptr).c_str(),0);