请教高手!关于程序自杀的问题,先上100分,能正常运行再送分!!!

taozc 2003-02-21 08:01:36
发现如下代码,据说运行后EXE文件会自己删除:
#include <windows.h>

int main()
{
char buf[MAX_PATH];
HMODULE Hm1,Hm2;
HANDLE Ex,De,Un;
Hm2=GetModuleHandle(0);
GetModuleFileName(Hm2,buf,255);
Hm1=GetModuleHandle("Kernel32");
Ex=GetProcAddress(Hm1,"ExitProcess");
De=GetProcAddress(Hm1,"DeleteFileA");
Un=GetProcAddress(Hm1,"UnmapViewOfFile");
CloseHandle(HANDLE(4));
__asm {
LEA EAX,buf
PUSH 0
PUSH 0
PUSH EAX
PUSH Ex
PUSH Hm2
PUSH De
PUSH Un
RET
}
return 0;
}

但是我运行后EXE文件还在。
我是在winXP,C++Builer6下。
这段代码能否在win9X/me/NT/2000/XP下运行。
...全文
101 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
taozc 2003-02-22
  • 打赏
  • 举报
回复
谢谢各位!
我今天再试试,也许只能用BAT方式才能实现多平台下的功能了。
albeta 2003-02-21
  • 打赏
  • 举报
回复
我提供一个可能可以实现的方法吧,不过没有代码哦。
2000/XP下,不是可以用什么远程线程么?就是把线程扣在别的程序或dll的空间里头,写一个这种线程,应该可以删除本身。
NowCan 2003-02-21
  • 打赏
  • 举报
回复
对于这个问题最好还是用bat文件来做,其他的方法总是有平台限制。
NowCan 2003-02-21
  • 打赏
  • 举报
回复
taozc(陶),编译不过是因为符号不认识。你也要GetProcAddress先。
jbz 2003-02-21
  • 打赏
  • 举报
回复
我用bat文件的方法早就实现了,在98、2000、xp下都可以的,你是否需要?
tccsdn 2003-02-21
  • 打赏
  • 举报
回复
安装与卸载之卸载篇---程序自杀
陈经韬

电脑用得久了,安装的软件乱七八糟,怎么办?一般我们会打开控制面版里的添加删除程序选项,选择相应的卸载选项就可以了。但如果我们能在自己的程序中增加一个删除功能来实现“自杀”,则会令软件增色不少。
有点电脑常识的人都知道,在Windows下如果程序正在运行,那么是无法将其删除的,当然,也不是绝对不可以。CIH大家应该知道吧,它的核心有两个,一个就是取得Ring0级权限,然后就可以随便干想干的事(比如说修改正在运行的文件),而我们一般的程序是运行在Ring3级上的。顺便提一句---NT下没有Ring的概念,所以CIH对其无效。用Delphi内镶汇编也可以取得Ring0级权限,但如果我们的程序运行在NT或者Win2000下就没有效果了。在说句题外话,现在的编译器都很不错了,大多数程序员都编不出比编译器编译出的更理想的代码,象Delphi,如果将它的某些单元代码改用内镶汇编,在某些方面如字符串处理方面会提高5倍左右的效率,但NT不支持某些汇编代码,如果程序在NT下工作就会出错,怎么办?稳定第一!所以我们不用这个方法,而且,用这个方法有点杀鸡用牛刀的味道。
用过DOS的朋友应该还记得批处理文件吧,新建一个批处理文件a.bat,编辑其内容为:del %0,然后运行它,怎么样?a.bat把自己删除掉了!!!好,我们就用它来进行程序的“自杀”!
找一个EXE可执行文件,比如说abc.exe,新建一个批处理文件a.bat,编辑其内容为:
:pp
del abc.exe
if exist abc.exe goto pp
del %0
先运行abc.exe,再运行a.bat,然后将abc.exe退出,你会发现a.exe和a.bat都没有了!!!按照这个思路,我们可以在程序中根据文件名称写一个批处理,将上面的abc.exe换成自己的EXE文件名就可以了。运行Delphi,新建一个工程,添加一个Button到窗体上,点击Button,写下如下代码:

procedure TForm1.Button1Click(Sender: TObject);
var Selfname,BatFilename,s1,s2:string;
BatchFile: TextFile;
begin
Selfname:=Extractfilename(application.exename);//取EXE文件自己的名称
BatFilename:=ExtractFilePath(Application.ExeName)+ 'a.bat';//批处理文件名称
S1:='@del '+Selfname;
S2:='if exist '+Selfname+' goto pp';
assignfile(BatchFile,BatFilename);
rewrite(BatchFile);
writeln(BatchFile,':pp');
writeln(BatchFile,S1);
writeln(BatchFile,S2);
writeln(BatchFile,'@del %0');
closefile(BatchFile);
winexec(pchar(BatFilename),sw_hide);//隐藏窗口运行a.bat
application.Terminate;//退出程序
end;
那我们的事情是不是就完了?NO!上面的程序原理是对的,但如果你的程序是运行在系统目录下如Windows目录下或者Windows\System等目录下,除非你打开那个目录看着它删除,否则根本没法卸掉的。那怎么办?别急,我们请出一个函数CreateProcess,它的原型为:
BOOL CreateProcess(
LPCTSTR lpApplicationName, // pointer to name of executable module
LPTSTR lpCommandLine, // pointer to command line string
LPSECURITY_ATTRIBUTES lpProcessAttributes, // pointer to process security attributes
LPSECURITY_ATTRIBUTES lpThreadAttributes, // pointer to thread security attributes
BOOL bInheritHandles, // handle inheritance flag
DWORD dwCreationFlags, // creation flags
LPVOID lpEnvironment, // pointer to new environment block
LPCTSTR lpCurrentDirectory, // pointer to current directory name
LPSTARTUPINFO lpStartupInfo, // pointer to STARTUPINFO
LPPROCESS_INFORMATION lpProcessInformation // pointer to PROCESS_INFORMATION
);
这个函数和OpenProcess、ReadProcessMemory、WriteProcessMemory使用可以用来读取和修改内存数据,常用的游戏修改器就是用它。由于这些不是本文的重点所以这里不作详细介绍,感兴趣的读者可自行翻阅Delphi自带的帮助文件。用CreateProcess函数创建一个进程就可以完美的完成我们的“程序自杀”了。
运行Delphi,新建一个工程,添加一个Button到窗体上,全部代码如下:

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure My_DeleteMe; //自定义程序自杀过程
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
My_DeleteMe;
end;
procedure TForm1.My_DeleteMe; //程序自杀
//-----------------------------------------------------------
function GetShortName(sLongName: string): string; //转换长文件名
var
sShortName: string;
nShortNameLen: integer;
begin
SetLength(sShortName, MAX_PATH);
nShortNameLen := GetShortPathName(PChar(sLongName),
PChar(sShortName), MAX_PATH - 1);
if (0 = nShortNameLen) then
begin
// handle errors...
end;
SetLength(sShortName, nShortNameLen);
Result := sShortName;
end;
//-------------------------------------------------
var
BatchFile: TextFile;
BatchFileName: string;
ProcessInfo: TProcessInformation;
StartUpInfo: TStartupInfo;
begin
BatchFileName := ExtractFilePath(ParamStr(0)) + '$$a$$.bat';
AssignFile(BatchFile, BatchFileName);
Rewrite(BatchFile);
Writeln(BatchFile, ':try');
Writeln(BatchFile, 'del "' + GetShortName(ParamStr(0)) + '"');
Writeln(BatchFile, 'if exist "' + GetShortName(ParamStr(0)) + '"' + ' goto try');
Writeln(BatchFile, 'del %0');
Writeln(BatchFile, 'cls');
Writeln(BatchFile, 'exit');
CloseFile(BatchFile);
FillChar(StartUpInfo, SizeOf(StartUpInfo), $00);
StartUpInfo.dwFlags := STARTF_USESHOWWINDOW;
StartUpInfo.wShowWindow := SW_Hide;
if CreateProcess(nil, PChar(BatchFileName), nil, nil,
False, IDLE_PRIORITY_CLASS, nil, nil, StartUpInfo,
ProcessInfo) then
begin
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
end;
Application.Terminate;
end;
end.

补充:1、上面的批处理的 del %0等同于 del a.bat,用del a.bat则批处理文件必须为a.bat,用del %0则可以随意。
2、所有程序在Pwin98+Delphi5、Win2000+Delphi5下运行通过。
本文的标题为《安装与卸载之卸载篇》,下次将介绍如何用Delphi制作自己的安装程序。记得有一位著名的黑客说过:我从来不去找什么工具软件,需要的话就自己写一个。如果我们也持这种态度,则编程水平一定会越来越高。

★作者:

陈经韬
Http:Lovejingtao.126.com
E-Mail: Lovejingtao@21.cn.com
tccsdn 2003-02-21
  • 打赏
  • 举报
回复
先转载一篇文章,来自lu0。


下面的代码由Gary Nebbett写就.Gary Nebbett乃是WINDOWS NT/2000 NATIVE API
REFERENCE的作者.乃NT系统一等一的高手.下面就分析一些他的这段代码.
这段代码在PROCESS没有结束前就将启动PROCESS的EXE文件删除了.
int main(int argc, char *argv[])
{
HMODULE module = GetModuleHandle(0);
CHAR buf[MAX_PATH];
GetModuleFileName(module, buf, sizeof buf);
CloseHandle(HANDLE(4));
__asm {
lea eax, buf
push 0
push 0
push eax
push ExitProcess
push module
push DeleteFile
push UnmapViewOfFile
ret
}
return 0;
}
现在,我们先看一下堆栈中的东西
偏移 内容
24 0
20 0
16 offset buf
12 address of ExitProcess
8 module
4 address of DeleteFile
0 address of UnmapViewOfFile
调用RET返回到了UnmapViewOfFile,也就是栈里的偏移0所指的地方.当进入UnmapViewOfFile的流程时,栈里见到的是返回地址DeleteFile和HMODUL
module.也就是说调用完毕后返回到了DeleteFile的入口地址.当返回到DeleteFile时,看到了ExitProcess的地址,也就是返回地址.和参数EAX,而EAX则是buffer.buffer存的是EXE的文件名.由GetModuleFileName(module,
buf, sizeof
buf)返回得到.执行了DeleteFile后,就返回到了ExitProcess的函数入口.并且参数为0而返回地址也是0.0是个非法地址.如果返回到地址0则会出错.而调用ExitProcess则应该不会返回.

这段代码的精妙之处在于:
1.如果有文件的HANDLE打开,文件删除就会失败,所以,CloseHandle(HANDLE(4));是十分巧妙的一手.HANDLE4是OS的硬编码,对应于EXE的IMAGE.在缺省情况下,OS假定没有任何调用会关闭IMAGE SECTION的HANDLE,而现在,该HANDLE被关闭了.删除文件就解除了文件对应的一个句柄.
2.由于UnmapViewOfFile解除了另外一个对应IMAGE的HANDLE,而且解除了IMAGE在内存的映射.所以,后面的任何代码都不可以引用IMAGE映射地址内的任何代码.否则就OS会报错.而现在的代码在UnmapViewOfFile后则刚好没有引用到任何IMAGE内的代码.

3.在ExitProcess之前,EXE文件就被删除了.也就是说,进程尚在,而主线程所在的EXE文件已经没了.(WINNT/9X都保护这些被映射到内存的WIN32 IMAGE不被删除.)
Gary Nebbett果然是WIN系列平台的顶尖高手之一.能写出如此代码.独辟蹊径啊:)

但是这段代码在C++Builder里不能通过编译,要做一下修改,如下。

#include <windows.h>

int main()
{
char buf[MAX_PATH];
HMODULE Hm1,Hm2;
HANDLE Ex,De,Un;
Hm2=GetModuleHandle(0);
GetModuleFileName(Hm2,buf,255);
Hm1=GetModuleHandle("Kernel32");
Ex=GetProcAddress(Hm1,"ExitProcess");
De=GetProcAddress(Hm1,"DeleteFileA");
Un=GetProcAddress(Hm1,"UnmapViewOfFile");
CloseHandle(HANDLE(4));
__asm {
LEA EAX,buf
PUSH 0
PUSH 0
PUSH EAX
PUSH Ex
PUSH Hm2
PUSH De
PUSH Un
RET
}
return 0;
}
taozc 2003-02-21
  • 打赏
  • 举报
回复
哪位能够调试运行成功,把代码贴出来让大家看看。
taozc 2003-02-21
  • 打赏
  • 举报
回复
我又找到一段代码,但在C++ Builder下不能编译通过,请各位看看,能不能实现98/2000/XP下的正常运行?
/////////////////////////////////////////////////////////////////////////
//98,2000下通过
//nt/2000下面的删除代码方法来自陆麟(lu0)的文章,再此表示感谢

#pragma optimize( "", off )
/*NOTE fun_AfterDelSelf MUST BE memory allocate by HeapAlloc or VirtualAlloc,and you should free it your self.
,can't use normal callback function(which data on diskdrive,and can't access it after we delete ourself
*/

int DeleteSelf(void * fun_AfterDelSelf)//
{
typedef int (WINAPI *PFClose)(LPVOID);
OSVERSIONINFO os_info;
os_info.dwOSVersionInfoSize=sizeof(os_info);
LPVOID pBuffer=NULL;
PFClose pClose;
PFClose pDelete;
char fn[4096];
HINSTANCE hins=GetModuleHandle(NULL);
GetModuleFileName(NULL,fn,4096);
if(!GetVersionEx(&os_info))
 return false;

switch(os_info.dwPlatformId)
{
case VER_PLATFORM_WIN32_NT:
 __try{
  while(CloseHandle((HANDLE)4));
 }__except(1){
 }
 CloseHandle((HANDLE)4);
 pClose=PFClose(UnmapViewOfFile);
 break;
case VER_PLATFORM_WIN32_WINDOWS:
 pClose=PFClose(FreeLibrary);
 break;
default:
 return false;
}
pDelete=PFClose(DeleteFile);
pBuffer=VirtualAlloc(NULL,4096,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
__asm{
 call _delete_end
}
 __asm{
_test_close:
 push hins
 call [pClose]
 or eax,eax
 jz _test_close
 lea eax,fn
 push eax
 call [pDelete]
 mov eax,fun_AfterDelSelf
 or eax,eax
 jz _Exit_Process
 call eax
_Exit_Process:
 push 0
 push MEM_RELEASE
 push 0
 push pBuffer
 
 push ExitProcess
 push VirtualFree
 ret
}
_delete_end:
__asm{
 pop ebx
 push 128
 push ebx
 push [pBuffer]
 call memcpy
 jmp pBuffer
}
return 0;
}
#pragma optimize( "", on )
/////////////////////////////////////////////////////////////////////////
icuc88 2003-02-21
  • 打赏
  • 举报
回复
呵呵,我的代码不是这样的。
但是,还是不行!
kingfish 2003-02-21
  • 打赏
  • 举报
回复
找了台xp试了试,的确不行
taozc 2003-02-21
  • 打赏
  • 举报
回复
kingfish(八百里秦川@龙城异客) :谢谢提醒,2000能运行的,XP下也应该可以呀,不知为什么不行?
taozc 2003-02-21
  • 打赏
  • 举报
回复
cscer(石头) :你的代码和我贴出来的不是一样的吗?
taozc 2003-02-21
  • 打赏
  • 举报
回复
我在2000试过了,确实可以,但是我需要能在98/ME/2000/XP下都能运行的程序,不知有没有现成的代码?
cscer 2003-02-21
  • 打赏
  • 举报
回复
#include <windows.h>

int main()
{
char buf[MAX_PATH];
HMODULE Hm1,Hm2;
HANDLE Ex,De,Un,aaa;
Hm2=GetModuleHandle(0);
GetModuleFileName(Hm2,buf,255);
Hm1=GetModuleHandle("Kernel32");
Ex=GetProcAddress(Hm1,"ExitProcess");
De=GetProcAddress(Hm1,"DeleteFileA");
Un=GetProcAddress(Hm1,"UnmapViewOfFile");
aaa = (HANDLE)4;
CloseHandle(aaa);
__asm {
LEA EAX,buf
PUSH 0
PUSH 0
PUSH EAX
PUSH Ex
PUSH Hm2
PUSH De
PUSH Un
RET
}
return 0;
}

kingfish 2003-02-21
  • 打赏
  • 举报
回复
刚试过
taozc 2003-02-21
  • 打赏
  • 举报
回复
kingfish(八百里秦川@龙城异客) :
你在2000下试过吗?
kingfish 2003-02-21
  • 打赏
  • 举报
回复
win2000下可以

jbz 2003-02-21
  • 打赏
  • 举报
回复
为了简单化,上面的代码也可以去掉getcommand(),直接用:
WinExec(BatFilename.c_str(),SW_HIDE); 也可以达到目的的。

但是这样做会在内存中留下一个垃圾(用查看进程可知)。
不过对于程序自杀来说可以无须计较了。

上面的代码修改一下也可以实现“软件重新启动”(自己启动自己)的功能。
这样的话getcommand()就有必要了。
jbz 2003-02-21
  • 打赏
  • 举报
回复
下面是我测试过的“自杀”代码。在98、2000、xp下运行正常。
请拷贝到你的工程里测试一下吧。
//************************************************************
//获得command命令的全路径
String getcommand()
{
//返回WINDOWS目录
String cmdfile,winpath,sysdir;
char str[MAX_PATH];
GetWindowsDirectory(str,MAX_PATH);
winpath=IncludeTrailingBackslash(str);
//获得WINDOWS系统目录system
GetSystemDirectory(str,MAX_PATH);
sysdir=str;
//获得WINDOWS command命令文件
if(GetEnvironmentVariable("comspec",str,MAX_PATH))
cmdfile=String(str);
if(!FileExists(cmdfile))
{
cmdfile=winpath+"command.com";
if(!FileExists(cmdfile))
{
cmdfile=sysdir+"cmd.exe";
if(!FileExists(cmdfile))return NULL;
}
}
return ExtractShortPathName(cmdfile);
}
//---------------------------------------------------------------------------
//自杀代码
void __fastcall TForm1::Button1Click(TObject *Sender)
{

TStringList *TempList = new TStringList;
AnsiString Selfname,BatFilename,CommandFile;
//取EXE文件自己的名称
Selfname=ExtractShortPathName(Application->ExeName);
//批处理文件名称
BatFilename=ExtractFilePath(Selfname)+"a.bat";
TempList->Clear();
TempList->Add(":pp");
TempList->Add("del "+Selfname);
TempList->Add("if exist "+Selfname+" goto pp");
TempList->Add("del %0");
TempList->SaveToFile(BatFilename);
delete TempList;
//隐藏窗口运行a.bat
CommandFile=getcommand();
if(CommandFile==NULL)WinExec(BatFilename.c_str(),SW_HIDE);
else WinExec((CommandFile+" /c "+BatFilename).c_str(),SW_HIDE);
//退出程序
Application->Terminate();
}
//---------------------------------------------------------------------------
//************************************************************

13,825

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder相关内容讨论区
社区管理员
  • 基础类社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧