自删除程序在 C 中运行良好,但在 C++ 项目中出错
Self-deleting program works fine in C but error in C++ project
我需要一种在我的 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
文件引入,然后与头文件链接,但是头方法破坏了所有其他方法windows 头文件(可能是因为编译器无法区分 c 头文件和 cpp 头文件?)但除此之外,我在这里遗漏了什么?
FARPROC
在 C++ 中的工作方式与在 C 中的工作方式不同。这实际上在 CallWindowProc
documentation:
中进行了描述
...
The FARPROC
type is declared as follows:
int (FAR WINAPI * FARPROC) ()
In C, the FARPROC
declaration indicates a callback function that has an unspecified parameter list. In C++, however, the empty parameter list in the declaration indicates that a function has no parameters. This subtle distinction can break careless code.
...
因此,您将不得不使用适当的函数指针签名,例如:
typedef struct _SELFDEL
{
...
DWORD (WINAPI *fnWaitForSingleObject)(HANDLE, DWORD);
BOOL (WINAPI *fnCloseHandle)(HANDLE);
BOOL (WINAPI *fnDeleteFile)(LPCTSTR);
void (WINAPI *fnSleep)(DWORD);
void (WINAPI *fnExitProcess)(UINT);
BOOL (WINAPI *fnRemoveDirectory)(LPCTSTR);
DWORD (WINAPI *fnGetLastError)();
...
} SELFDEL;
SELFDEL local;
...
local.fnWaitForSingleObject = &WaitForSingleObject;
local.fnCloseHandle = &CloseHandle;
local.fnDeleteFile = &DeleteFile;
local.fnSleep = &Sleep;
local.fnExitProcess = &ExitProcess;
local.fnRemoveDirectory = &RemoveDirectory;
local.fnGetLastError = &GetLastError;
...
如评论所述,这里的问题是代码在编译为 C++ 时无效,因为函数原型不正确。您可以通过为文件提供 .c
后缀来强制编译器将代码编译为 C。
然后必须通知您的调用 C++ 代码 SelfDelete
是 C 函数而不是 C++ 函数。这是因为 C++ 函数具有 mangled names 以允许重载之类的事情。为此,您可以在 C++ 代码中将函数声明为 extern "C"
:
extern "C" BOOL SelfDelete(BOOL fRemoveDirectory);
只需将其放入您的调用 C++ 代码中,在您实际调用该函数之前的某处。没有这个,你的程序将不会 link,因为 C 代码和 C++ 代码将不同意在幕后实际调用的函数。
我需要一种在我的 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
文件引入,然后与头文件链接,但是头方法破坏了所有其他方法windows 头文件(可能是因为编译器无法区分 c 头文件和 cpp 头文件?)但除此之外,我在这里遗漏了什么?
FARPROC
在 C++ 中的工作方式与在 C 中的工作方式不同。这实际上在 CallWindowProc
documentation:
...
TheFARPROC
type is declared as follows:
int (FAR WINAPI * FARPROC) ()
In C, the
FARPROC
declaration indicates a callback function that has an unspecified parameter list. In C++, however, the empty parameter list in the declaration indicates that a function has no parameters. This subtle distinction can break careless code.
...
因此,您将不得不使用适当的函数指针签名,例如:
typedef struct _SELFDEL
{
...
DWORD (WINAPI *fnWaitForSingleObject)(HANDLE, DWORD);
BOOL (WINAPI *fnCloseHandle)(HANDLE);
BOOL (WINAPI *fnDeleteFile)(LPCTSTR);
void (WINAPI *fnSleep)(DWORD);
void (WINAPI *fnExitProcess)(UINT);
BOOL (WINAPI *fnRemoveDirectory)(LPCTSTR);
DWORD (WINAPI *fnGetLastError)();
...
} SELFDEL;
SELFDEL local;
...
local.fnWaitForSingleObject = &WaitForSingleObject;
local.fnCloseHandle = &CloseHandle;
local.fnDeleteFile = &DeleteFile;
local.fnSleep = &Sleep;
local.fnExitProcess = &ExitProcess;
local.fnRemoveDirectory = &RemoveDirectory;
local.fnGetLastError = &GetLastError;
...
如评论所述,这里的问题是代码在编译为 C++ 时无效,因为函数原型不正确。您可以通过为文件提供 .c
后缀来强制编译器将代码编译为 C。
然后必须通知您的调用 C++ 代码 SelfDelete
是 C 函数而不是 C++ 函数。这是因为 C++ 函数具有 mangled names 以允许重载之类的事情。为此,您可以在 C++ 代码中将函数声明为 extern "C"
:
extern "C" BOOL SelfDelete(BOOL fRemoveDirectory);
只需将其放入您的调用 C++ 代码中,在您实际调用该函数之前的某处。没有这个,你的程序将不会 link,因为 C 代码和 C++ 代码将不同意在幕后实际调用的函数。