【发布时间】:2025-12-27 13:05:07
【问题描述】:
我需要一种在我的 Win32 C++ 项目中自删除可执行文件的方法,我找到了一个用 C 语言执行此操作的程序:
selfdel.c:
// http://www.catch22.net/tuts/win32/self-deleting-executables
// selfdel.c
//
// Self deleting executable for Win9x/WinNT (works for all versions of windows)
//
// J Brown 1/10/2003
//
// This source file must be compiled with /GZ turned OFF
// (basically, disable run-time stack checks)
//
// Under debug build this is always on (MSVC6)
//
//
/**
* The way this works is:
* Firstly a child process is created in a suspended state (any process will do - i.e. explorer.exe).
* Some code is then injected into the address-space of the child process.
* The injected code waits for the parent-process to exit.
* The parent-process is then deleted.
* The injected code then calls ExitProcess, which terminates the child process.
*/
#include <windows.h>
#include <tchar.h>
#pragma pack(push, 1)
#define CODESIZE 0x200
//
// Structure to inject into remote process. Contains
// function pointers and code to execute.
//
typedef struct _SELFDEL
{
struct _SELFDEL *Arg0; // pointer to self
BYTE opCodes[CODESIZE]; // code
HANDLE hParent; // parent process handle
FARPROC fnWaitForSingleObject;
FARPROC fnCloseHandle;
FARPROC fnDeleteFile;
FARPROC fnSleep;
FARPROC fnExitProcess;
FARPROC fnRemoveDirectory;
FARPROC fnGetLastError;
BOOL fRemDir;
TCHAR szFileName[MAX_PATH]; // file to delete
} SELFDEL;
#pragma pack(pop)
#ifdef _DEBUG
#define FUNC_ADDR(func) (PVOID)(*(DWORD *)((BYTE *)func + 1) + (DWORD)((BYTE *)func + 5))
#else
#define FUNC_ADDR(func) func
#endif
//
// Routine to execute in remote process.
//
static void remote_thread(SELFDEL *remote)
{
// wait for parent process to terminate
remote->fnWaitForSingleObject(remote->hParent, INFINITE);
remote->fnCloseHandle(remote->hParent);
// try to delete the executable file
while(!remote->fnDeleteFile(remote->szFileName))
{
// failed - try again in one second's time
remote->fnSleep(1000);
}
// finished! exit so that we don't execute garbage code
remote->fnExitProcess(0);
}
//
// Delete currently running executable and exit
//
BOOL SelfDelete(BOOL fRemoveDirectory)
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
CONTEXT context;
DWORD oldProt;
SELFDEL local;
DWORD entrypoint;
TCHAR szExe[MAX_PATH] = _T("explorer.exe");
//
// Create executable suspended
//
if(CreateProcess(0, szExe, 0, 0, 0, CREATE_SUSPENDED|IDLE_PRIORITY_CLASS, 0, 0, &si, &pi))
{
local.fnWaitForSingleObject = (FARPROC)WaitForSingleObject;
local.fnCloseHandle = (FARPROC)CloseHandle;
local.fnDeleteFile = (FARPROC)DeleteFile;
local.fnSleep = (FARPROC)Sleep;
local.fnExitProcess = (FARPROC)ExitProcess;
local.fnRemoveDirectory = (FARPROC)RemoveDirectory;
local.fnGetLastError = (FARPROC)GetLastError;
local.fRemDir = fRemoveDirectory;
// Give remote process a copy of our own process handle
DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(),
pi.hProcess, &local.hParent, 0, FALSE, 0);
GetModuleFileName(0, local.szFileName, MAX_PATH);
// copy in binary code
memcpy(local.opCodes, FUNC_ADDR(remote_thread), CODESIZE);
//
// Allocate some space on process's stack and place
// our SELFDEL structure there. Then set the instruction pointer
// to this location and let the process resume
//
context.ContextFlags = CONTEXT_INTEGER|CONTEXT_CONTROL;
GetThreadContext(pi.hThread, &context);
// Allocate space on stack (aligned to cache-line boundary)
entrypoint = (context.Esp - sizeof(SELFDEL)) & ~0x1F;
//
// Place a pointer to the structure at the bottom-of-stack
// this pointer is located in such a way that it becomes
// the remote_thread's first argument!!
//
local.Arg0 = (SELFDEL *)entrypoint;
context.Esp = entrypoint - 4; // create dummy return address
context.Eip = entrypoint + 4; // offset of opCodes within structure
// copy in our code+data at the exe's entry-point
VirtualProtectEx(pi.hProcess, (PVOID)entrypoint, sizeof(local), PAGE_EXECUTE_READWRITE, &oldProt);
WriteProcessMemory(pi.hProcess, (PVOID)entrypoint, &local, sizeof(local), 0);
FlushInstructionCache(pi.hProcess, (PVOID)entrypoint, sizeof(local));
SetThreadContext(pi.hThread, &context);
// Let the process continue
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return TRUE;
}
return FALSE;
}
使用 Visual Studio 开发人员命令提示符和 cl 单独编译它会创建一个可执行文件,它会按预期运行。把它放在我的 C++ 项目中会在remote_thread 中产生一个错误:
我无法弄清楚这个错误。在 x64 和 x86 中构建会导致相同的错误。我尝试将它单独放入其中,添加extern "C" {} 作为包装器,将文件作为.c 文件引入,然后与头文件链接,但随后 header 方法破坏了所有其他 Windows 头文件(可能是因为编译器无法区分 c 标头和 cpp 标头?)但除此之外,我在这里遗漏了什么?
【问题讨论】:
-
C 和 C++ 是不同的语言,C++ 有更严格的输入规则。
-
我相信这是由于 C 将
FARPROC解释为指向具有未指定数量参数的函数的指针,而 C++ 将相同的定义解释为指向具有 0 个参数的函数的指针。你能告诉我们FARPROC是如何定义的吗? IIRC,类似于typedef int* (*FARPROC)()。 -
@Brian 是正确的。尝试给这个文件一个
.c扩展名(以便编译器将其编译为 C 而不是 C++)。您发布的代码不是有效的 C++ ,因为SELFDEL中的函数原型没有指定正确数量和类型的参数。 -
您可以在您的 C++ 代码中将
SelfDelete声明为extern "C" BOOL SelfDelete(BOOL fRemoveDirectory);。 -
你实际上并不需要一个头文件,只需在你的 C++ 文件中,在你实际调用函数之前的某个地方。