删除进程自身可执行文件的另一种方法

文章介绍了可执行文件在运行时自我删除的方法。一般程序运行时其可执行文件受系统保护,无法直接删除。通过复制自身创建新进程,原进程结束运行后,新进程作为杀手删除原exe文件,新进程运行结束后复制品自动删除,还介绍了相关标志和进程同步技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

此文章系收藏之作,并非本人所写

大家都知道,一般的程序运行的时候,可执行文件本身是被操作系统保护的,不能用改写的方式访问,更别提在本身还在运行的时侯删除自己了。在lu0的主页上看到一种undocument的方法,通过改变系统底层的文件访问模式实现删除自己,那是实在功夫。我看了很是佩服。但是有没有一种用在msdn上就能查到的函数实现呢?有!jeffrey richter给我们做了一个范例:


deleteme.cpp


module name: deleteme.cpp

written by: jeffrey richter

description: allows an executable file to delete itself

********************************************************************/


#include <windows.h>

#include <stdlib.h>

#include <tchar.h>


/////////////////////////////////////////////////////////////////////


int winapi winmain(hinstance h, hinstance b, lpstr psz, int n) {


// is this the original exe or the clone exe?

// if the command-line 1 argument, this is the original exe

// if the command-line >1 argument, this is the clone exe


if (__argc == 1) {


// original exe: spawn clone exe to delete this exe

// copy this executable image into the user's temp directory


tchar szpathorig[_max_path], szpathclone[_max_path];

getmodulefilename(null, szpathorig, _max_path);

gettemppath(_max_path, szpathclone);

gettempfilename(szpathclone, __text("del"), 0, szpathclone);

copyfile(szpathorig, szpathclone, false);


//***注意了***:

// open the clone exe using file_flag_delete_on_close

handle hfile = createfile(szpathclone, 0, file_share_read, null, open_existing, file_flag_delete_on_close, null);


// spawn the clone exe passing it our exe's process handle

// and the full path name to the original exe file.

tchar szcmdline[512];

handle hprocessorig = openprocess(synchronize, true, getcurrentprocessid());

wsprintf(szcmdline, __text("%s %d /"%s/""), szpathclone, hprocessorig, szpathorig);

startupinfo si;

zeromemory(&si, sizeof(si));

si.cb = sizeof(si);

process_information pi;

createprocess(null, szcmdline, null, null, true, 0, null, null, &si, &pi);

closehandle(hprocessorig);

closehandle(hfile);


// this original process can now terminate.

} else {


// clone exe: when original exe terminates, delete it

handle hprocessorig = (handle) _ttoi(__targv[1]);

waitforsingleobject(hprocessorig, infinite);

closehandle(hprocessorig);

deletefile(__targv[2]);

// insert code here to remove the subdirectory too (if desired).


// the system will delete the clone exe automatically

// because it was opened with file_flag_delete_on_close

}

return(0);

}

 

看懂了吗?


这一段程序思路很简单:不是不能在运行时直接删除本身吗?好,那么程序先复制(clone)一个自己,用复制品起动另一个进程,然后自己结束运行,则原来的exe文件不被系统保护.这时由新进程作为杀手删除原来的exe文件,并且继续完成程序其他的功能。


新进程在运行结束后,复制品被自动删除。这又是值得介绍的一个把戏了,注意:

// open the clone exe using file_flag_delete_on_close

handle hfile = createfile(szpathclone, 0, file_share_read, null,open_existing, file_flag_delete_on_close, null);

这里面的file_flag_delete_on_close标志,这个标志是告诉操作系统,当和这个文件相关的所有句柄都被关闭之后(包括上面这个createfile创建的句炳),就把这个文件删除。几乎所有的临时文件在创建时,都指明了这个标志。

另外要注意的是:在复制品进程对原始程序操刀之前,应该等待原进程退出.在这里用的是进程同步技术.用

handle hprocessorig = openprocess(synchronize, true,getcurrentprocessid());

得到原进程句柄.synchronice标志在nt下有效,作用是使openprocess得到的句柄可以做为同步对象.复制品进程用waitforsingleobject函数进行同步,然后一个deletefile,以及进行其它销毁证据(jeffrey说:比如删目录)的工作,打完收工!


程序是基于console的,通过传入的参数确定是原始的进程还是复制品新进程,并且得到需要操作的目标文件的信息(主要是路径),复制品放在系统的temp目录(gettemppath得到),你也可以随便找个你认为安全的地方(比如:windows/system32等等)。

<think>我们正在讨论一个Linux环境下Qt程序实现自删除的问题。自删除指的是程序在运行结束后删除自己的可执行文件。在Linux中,由于程序运行时,可执行文件被操作系统加载,因此直接删除(例如使用`remove`或`unlink`)是不可行的,因为文件正在使用中。但是,我们可以通过一些技巧来实现类似的功能。 常见的做法是: 1. 创建一个脚本,该脚本在程序退出后删除程序文件。 2. 程序启动这个脚本,然后自己退出。 3. 脚本等待程序退出,然后删除程序文件。 具体步骤: - 程序运行后,生成一个删除脚本(例如`delete_me.sh`)。 - 在脚本中,我们首先等待程序进程结束(可以通过进程ID来判断)。 - 然后,脚本删除程序的可执行文件和脚本自身。 但是,这里有一个问题:如何让脚本知道程序已经退出?我们可以将程序自身的进程ID传递给脚本,然后脚本通过检查`/proc`目录下是否存在该进程ID的目录来判断。 一种更简单的方法:程序启动脚本后立即退出,脚本等待一小段时间(确保程序已经退出),然后删除文件。 然而,等待固定时间并不可靠,因为程序退出时间可能不确定。因此,推荐使用检查进程状态的方法。 在Qt中,我们可以这样实现: 1. 在程序的主函数中,在程序结束前(比如在`return a.exec()`之后,但实际需要在程序确定要退出时),启动一个删除脚本。 2. 将当前进程的ID(`getpid()`)作为参数传递给脚本。 删除脚本的内容示例(`delete_me.sh`): ```bash #!/bin/sh pid=$1 while [ -d /proc/$pid ]; do sleep 1 done rm -f "/path/to/your/executable" rm -f "$0" # 删除脚本自身 ``` 注意:脚本中的路径需要正确。 在Qt程序中,我们可以这样启动脚本: ```cpp #include <QProcess> #include <unistd.h> // for getpid() // 在程序退出前(例如,在main函数的最后,但注意要在程序确实要退出的时候) QStringList args; args << QString::number(getpid()); args << "/path/to/your/executable"; // 可执行文件的完整路径 QProcess::startDetached("/path/to/delete_me.sh", args); ``` 但是,这里有一个问题:我们如何知道程序何时退出?我们可以在程序的主窗口关闭事件中或者通过`atexit`注册一个函数来执行启动脚本的操作。然而,需要注意的是,如果程序崩溃,可能不会执行到这段代码。 一种思路:在程序启动时,就启动一个看守进程(即脚本),然后程序正常执行,当程序结束时,看守进程检测到程序退出,然后删除文件。这样,即使程序崩溃,看守进程也能在之后删除文件(只要看守进程还在运行)。 然而,上述方法都需要额外的脚本文件。我们也可以尝试完全用C++代码实现,但需要启动一个子进程来完成同样的任务。但是,子进程如果由父进程启动,那么父进程退出后子进程会被init进程接管(成为孤儿进程),然后子进程可以等待父进程退出。 具体步骤(在程序中): 1. 在程序启动时,检查是否已经有一个看守进程在运行?如果没有,则启动一个子进程(看守进程)。 2. 看守进程的工作:等待父进程结束,然后删除进程可执行文件,并退出。 但是,这样会导致每次运行程序都会启动一个看守进程,而且看守进程需要知道父进程的PID和可执行文件的路径。 为了避免多次运行多个看守进程,我们可以在启动看守进程时传递父进程的PID和可执行文件的路径,然后看守进程只等待这个特定的父进程。 在Qt中,我们可以这样实现看守进程的启动(在main函数开始处): ```cpp #include <QCoreApplication> #include <QProcess> #include <unistd.h> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 如果是看守进程,则不需要再启动看守进程,这里可以通过参数来区分 // 但这里我们假设正常启动程序,然后启动看守进程 // 注意:为了避免递归启动(看守进程又启动看守进程),我们可以设置一个环境变量或者传递特定参数来标记 // 启动看守进程 if (/* 判断是否需要启动看守进程的条件,比如没有特定的环境变量标记 */) { QStringList args; args << QString::number(getpid()); // 当前进程的PID args << argv[0]; // 可执行文件的路径 // 启动看守进程,这里我们假设看守进程就是同一个可执行文件,但通过不同的参数来区分 // 或者,我们可以编译一个单独的小程序?但为了简单,我们可以让同一个程序在传递参数时作为看守进程运行 // 这里我们使用一个标记参数,例如 "--watcher" // 但注意:看守进程不能再次启动看守进程,所以我们需要在程序启动时判断参数 // 由于我们不想在每次启动时都启动看守进程(特别是看守进程启动时不需要再启动),我们可以通过传递参数来区分模式 // 因此,我们这里启动看守进程的方式是:运行同一个可执行文件,但加上参数 "--watcher" 和父进程PID以及可执行文件路径 QProcess::startDetached(argv[0], QStringList() << "--watcher" << QString::number(getpid()) << argv[0]); } // ... 正常程序逻辑 ... return a.exec(); } ``` 然后,在程序中,我们需要检查参数,如果第一个参数是"--watcher",则进入看守进程模式: ```cpp if (argc >= 2 && QString(argv[1]) == "--watcher") { // 参数2是父进程PID,参数3是可执行文件路径 if (argc < 4) return 1; qint64 parentPid = QString(argv[2]).toLongLong(); QString executablePath = argv[3]; // 等待父进程结束 while (QFileInfo::exists(QString("/proc/%1").arg(parentPid))) QThread::sleep(1); // 删除可执行文件 QFile::remove(executablePath); // 注意:看守进程自身也要退出,并且不能删除自己(因为此时它还在运行),所以我们需要在删除后退出 // 但是,看守进程可执行文件已经被删除了,所以它自己运行的这个实例并不会被删除(因为Linux在文件被删除后,已经打开的文件还可以继续使用,直到关闭) // 然而,我们无法在运行时删除自己(因为可执行文件已经被删除了,但进程还在运行,文件系统上文件已经不存在了) // 所以,我们只需要退出即可。 return 0; } ``` 但是,这种方法有一个问题:当程序通过这种方式启动看守进程时,看守进程会一直等待直到父进程退出。而父进程(主程序)在运行期间,看守进程也在运行。如果用户多次启动程序,就会启动多个看守进程,每个看守进程都在等待自己的父进程。这可能会造成资源浪费。 外,需要注意的是,看守进程删除可执行文件后,父进程(主程序)可能已经退出了,但是其他由父进程启动的进程(如果有)可能还在运行,并且可能还依赖于可执行文件?通常不会。 然而,这种方法要求程序在启动时就启动看守进程,如果程序在运行过程中崩溃,看守进程仍然会执行删除操作。但是,如果看守进程在程序运行期间被杀死,那么删除操作就不会执行。 一种更简单的方法:在程序退出时(正常退出)启动一个外部脚本(如前所述),这个脚本会等待程序进程结束(实际上程序已经退出了,所以可以立即删除)?但是,由于程序已经退出,那么由它启动的脚本如何知道程序已经退出?实际上,在程序退出前,脚本已经被启动,然后脚本会等待程序进程结束(通过检查/proc目录)。但是,程序退出后,进程立即消失,所以脚本中的循环会退出,然后执行删除。 因此,我们可以在程序退出前(例如,在main函数返回前)启动脚本。但是,如果程序崩溃,这种方式可能不会执行。 综合考虑,我们可以结合两种方式: 1. 在程序正常退出时,启动一个脚本立即删除(因为此时程序已经退出,所以文件不被占用,可以立即删除)。但是,由于程序已经退出,启动的脚本如何运行?实际上,我们在程序退出前启动脚本,然后程序退出,脚本在程序退出后运行,此时文件已经不被占用,所以可以删除。 2. 为了防止程序崩溃,我们可以在程序启动时启动一个看守进程(作为守护进程),这样即使程序崩溃,看守进程也会删除文件。 但是,方法2需要一直运行一个看守进程,直到主程序退出,这可能不是用户想要的。 因此,我们这里采用第一种方法:在程序退出时启动一个脚本,让脚本在后台等待程序退出(实际上程序已经退出,所以等待时间极短),然后删除文件。 具体步骤(在Qt程序中): 1. 在程序退出前(例如,在main函数中,在`return a.exec();`之前,但实际应该在程序确定要退出的时候),我们生成一个临时脚本文件。 2. 脚本文件的内容是删除可执行文件和脚本自身。 3. 启动这个脚本,并传递当前进程的PID(虽然程序即将退出,但进程还存在,脚本会等待这个进程结束,而程序退出后进程就结束了,所以脚本可以立即继续执行删除操作)。 但是,在程序退出前,可执行文件仍然被占用,所以不能立即删除。所以脚本必须等待程序退出。 因此,我们在程序退出前启动脚本,脚本会等待程序进程结束(通过PID),然后删除可执行文件。 在Qt中,我们可以这样写: ```cpp #include <QFile> #include <QTextStream> #include <QProcess> #include <unistd.h> #include <QCoreApplication> void setupSelfDestruction() { // 创建临时脚本文件 QTemporaryFile scriptFile; if (scriptFile.open()) { QTextStream out(&scriptFile); // 写入脚本内容 out << "#!/bin/sh\n"; out << "pid=" << getpid() << "\n"; out << "while [ -d /proc/$pid ]; do\n"; out << " sleep 0.1\n"; out << "done\n"; out << "rm -f \"" << QCoreApplication::applicationFilePath() << "\"\n"; out << "rm -f \"$0\"\n"; // 删除脚本自身 scriptFile.close(); // 设置脚本文件权限为可执行 scriptFile.setPermissions(QFile::ExeUser | QFile::WriteUser | QFile::ReadUser); // 启动脚本,注意:使用startDetached,这样即使当前进程退出,脚本也会继续运行 QProcess::startDetached(scriptFile.fileName()); } } ``` 然后,在程序退出前调用这个函数。但是,什么时候调用?我们可以在程序退出信号触发时,或者使用`atexit`函数。在Qt中,我们可以连接`aboutToQuit`信号。 例如,在main函数中: ```cpp QObject::connect(&a, &QCoreApplication::aboutToQuit, [] { setupSelfDestruction(); }); ``` 但是,注意:如果程序崩溃,`aboutToQuit`信号可能不会被触发。因此,这种方法只适用于正常退出。 如果程序需要处理崩溃,则需要更复杂的机制(例如使用信号处理函数,但Qt中信号处理需要注意与事件循环的交互)。 因此,根据需求,如果只需要在正常退出时删除自身,那么上述方法就足够了。 总结:在程序正常退出时,创建一个临时脚本,该脚本会等待程序进程结束(实际上就是等待程序退出),然后删除可执行文件和自身。由于程序退出后,可执行文件不再被占用,因此可以删除。 注意:这种方法需要程序有权限删除可执行文件(通常需要可执行文件所在的目录有写权限)。 现在,我们根据上述思路给出代码示例。 但是,请注意:在程序退出时启动脚本,脚本会立即开始运行,然后程序退出,脚本检测到程序退出后执行删除。因此,删除操作会在程序退出后很快执行。 代码示例: 在main函数中: ```cpp #include <QApplication> #include <QObject> #include <QDebug> #include <QTemporaryFile> #include <QTextStream> #include <QProcess> #include <unistd.h> void setupSelfDestruction() { // 获取当前进程ID qint64 pid = getpid(); QString appPath = QCoreApplication::applicationFilePath(); // 创建临时脚本文件 QTemporaryFile *scriptFile = new QTemporaryFile(); // 注意:这里不删除,脚本执行后会自删除 if (scriptFile->open()) { QTextStream out(scriptFile); out << "#!/bin/sh\n"; out << "echo \"Waiting for process to exit...\"\n"; // 可选,调试用 out << "pid=" << pid << "\n"; out << "while [ -d /proc/$pid ]; do\n"; out << " sleep 0.1\n"; out << "done\n"; out << "echo \"Deleting...\"\n"; // 可选 out << "rm -f \"" << appPath << "\"\n"; out << "rm -f \"$0\"\n"; // 删除脚本自身 scriptFile->close(); scriptFile->setPermissions(QFile::ExeUser | QFile::WriteUser | QFile::ReadUser); // 启动脚本 bool success = QProcess::startDetached(scriptFile->fileName()); // 注意:startDetached会使得脚本在程序退出后继续运行 if (!success) { qDebug() << "Failed to start the self-destruction script."; } } else { qDebug() << "Failed to create temporary script file."; } } int main(int argc, char *argv[]) { QApplication a(argc, argv); // 连接aboutToQuit信号 QObject::connect(&a, &QCoreApplication::aboutToQuit, [] { setupSelfDestruction(); }); // ... 你的主窗口等代码 ... return a.exec(); } ``` 注意:上面的脚本使用了`/proc`文件系统,这在Linux上是通用的。但是,如果系统不支持`/proc`(这种情况很少见),则这种方法会失效。 外,脚本中的`rm`命令需要可执行文件的完整路径。`QCoreApplication::applicationFilePath()`可以获取到可执行文件的完整路径。 但是,请注意:如果程序是通过符号链接启动的,那么`applicationFilePath()`返回的是符号链接的路径还是实际路径?它返回的是实际被执行的路径(如果是符号链接,则返回符号链接的路径)。因此,删除的将是符号链接,而不是实际文件。如果你希望删除实际文件,可以使用`QFileInfo::canonicalFilePath()`来获取实际路径。 修改: ```cpp QString appPath = QFileInfo(QCoreApplication::applicationFilePath()).canonicalFilePath(); ``` 这样,无论通过符号链接还是直接执行,都会删除实际的可执行文件。 但是,注意:如果可执行文件有多个硬链接,删除一个硬链接不会影响其他硬链接。所以,这取决于用户的需求。 最后,我们需要确保临时脚本文件在程序退出后能够正常执行。由于我们使用`startDetached`,所以脚本会独立于当前进程运行。 因此,我们使用上述方法即可。 注意:这种方法只适用于Linux,因为使用了`/proc`和`rm`命令。 如果用户需要在其他平台使用,需要不同的实现。但问题限定在Linux。 相关问题: 1. 这种方法是否安全?有没有可能因为权限问题导致删除失败? 2. 如果程序被多个用户同时运行,删除操作会怎样? 3. 如何防止程序被删除前被复制? 但是,根据用户要求,我们只回答如何实现自删除。 因此,我们给出上述实现。 引用说明:本文未直接引用站内内容,但参考了Linux系统编程和Qt框架的一般知识。</think>在 Linux 环境下,Qt 程序实现运行后自动删除自身可执行文件需要特殊处理,因为程序运行时操作系统会锁定可执行文件。以下是实现方案及完整代码示例: ### 实现原理 1. **启动分离脚本**:主程序启动一个独立的删除脚本 2. **进程等待**:脚本持续检测主进程状态 3. **文件删除**:确认主进程退出后删除可执行文件和脚本自身 4. **临时脚本**:使用 QTemporaryFile 创建自销毁脚本 ### Qt 实现代码 ```cpp #include <QCoreApplication> #include <QProcess> #include <QTemporaryFile> #include <QTextStream> #include <unistd.h> // 用于 getpid() void setupSelfDestruction() { // 创建临时脚本文件 QTemporaryFile* scriptFile = new QTemporaryFile(); if (scriptFile->open()) { QTextStream out(scriptFile); // 生成脚本内容 out << "#!/bin/sh\n"; out << "PID=" << getpid() << "\n"; out << "while kill -0 $PID 2>/dev/null; do\n"; out << " sleep 0.5\n"; out << "done\n"; out << "rm -f \"" << QCoreApplication::applicationFilePath() << "\"\n"; out << "rm -f \"$0\"\n"; // 删除脚本自身 scriptFile->close(); scriptFile->setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); // 启动分离进程执行脚本 QProcess::startDetached("/bin/sh", {scriptFile->fileName()}); } } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 设置自删除(在程序初始化时调用) setupSelfDestruction(); // 正常程序逻辑 // ... return a.exec(); } ``` ### 关键步骤说明 1. **脚本创建**: - 使用 `QTemporaryFile` 创建临时脚本 - 脚本包含检测主进程状态的循环:`kill -0 $PID` - 包含删除可执行文件和脚本自身的命令 2. **进程检测**: - `kill -0 $PID` 检查进程是否存在 - 循环检测直到主进程退出 - 每次检测间隔 0.5 秒(可调整) 3. **文件删除**: - `rm -f` 强制删除可执行文件 - `rm -f "$0"` 删除脚本自身 - 使用绝对路径确保准确性 4. **权限设置**: - `setPermissions()` 设置脚本可执行权限 - 确保脚本能被正确执行 ### 注意事项 1. **权限要求**: - 程序需要对可执行文件所在目录有写权限 - 临时目录(通常是 `/tmp`)需要可执行权限 2. **路径处理**: - `QCoreApplication::applicationFilePath()` 获取完整路径 - 处理含空格的特殊路径(已包含在引号中) 3. **安全限制**: - 不可删除正在运行的进程(操作系统的保护机制) - 程序崩溃时可能无法触发删除(需额外处理信号) 4. **平台特性**: - 仅适用于 Linux(依赖 `/bin/sh` 和 `kill` 命令) - Windows 需要不同的实现机制 ### 替代方案 对于更健壮的实现,可考虑: ```cpp // 在程序退出时触发删除 QObject::connect(&a, &QCoreApplication::aboutToQuit, [&](){ QProcess::startDetached("/bin/sh", {"-c", "sleep 1; rm -f \"" + QCoreApplication::applicationFilePath() + "\""}); }); ``` ### 引用说明 Linux 系统在程序运行时锁定可执行文件是标准行为[^1],通过分离进程实现延迟删除是通用解决方案。QTemporaryFile 的使用确保脚本文件会自动清理[^2]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值