Sunday, May 16, 2004

 

How to delete the program itself

本文根据他人文章总结而成

方法一:最简单的方法,也最不安全

在下次Windows启动时自动删除它,听说过Exe自杀吗?

wininit.ini可完成这个工作.wininit.ini文件一共有三个段,这里只介绍[rename]段的格式 :

[rename]
fileDec = fileSrc ; 把fileSrc改名为fileDec .
如果上面的fileDec等于nul,就是删除fileSrc文件了( 把fileSrc改名为NUL(L) ) .
比如这么写:
[rename]
c:\test.exe = nul ; 删除c:\test.exe文件
重新启动系统,Windows将在系统内核加载以前自动查找系统目录下有没有wininit.ini文件,如果存在则按他的要求执行.执行完毕后再删除wininit.ini本身. 执行期间屏幕上将显示下面的字样 :

Please wait while Setup updates your configuration files.
This may take a few minutes……

呵呵,这只能对付单系统的机子,如果双系统的话,你也防不了的,人家在98下掉电,启动到2k,一样搞你

The batch file method has its pros and cons.

Pros:
It can perform mutiple commands such as delete a file
AND remove its directory.

Cons:
Inelegance. You have to create a separate file. Also,
the deletion loop is a resource hog. It's not something
you'd want to brag about.

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

#include
#include

BOOL SelfDelete()
{
HANDLE hBatch;
DWORD dwBytesWritten=0;
TCHAR szModule [MAX_PATH],
szBatch [MAX_PATH],
szBuffer [1024];

// set file path names
GetTempPath(MAX_PATH,szBatch);
lstrcat(szBatch,"\\selfdel.bat");
GetModuleFileName(0,szModule,MAX_PATH);
GetShortPathName(szBatch,szBatch,MAX_PATH);
GetShortPathName(szModule,szModule,MAX_PATH);

// fill batch write buffer
lstrcpy(szBuffer,":Repeat\ndel ");
lstrcat(szBuffer,szModule);
lstrcat(szBuffer,"\nif exist ");
lstrcat(szBuffer,szModule);
lstrcat(szBuffer," goto Repeat\ndel ");
lstrcat(szBuffer,szBatch);

// create batch file
hBatch = CreateFile(szBatch,
GENERIC_WRITE,
FILE_SHARE_WRITE,
0,CREATE_ALWAYS,0,0);

// write and execute batch file
if((hBatch!=INVALID_HANDLE_VALUE) &&
(WriteFile(hBatch,szBuffer,
lstrlen(szBuffer),
&dwBytesWritten,0)) &&
(dwBytesWritten == (DWORD)lstrlen(szBuffer)) &&
(CloseHandle(hBatch)) &&
((INT)ShellExecute(0,0,szBatch,0,0,SW_HIDE)>32))
{
// notify explorer shell
SHChangeNotify(SHCNE_DELETE,SHCNF_PATH,szModule,0);
return TRUE;
}

else // if error condition
{
CloseHandle(hBatch);
DeleteFile(szBatch);
return FALSE;
}
}

方法二:对于本程序在内存中的其它映像未做考虑,只是Demo而已

近日看到网友询问如何实现程序运行之后把自己删除的方法,不知大家对木马甚么的兴趣实在太浓,还是想要这样的效果:用户只要一运行程序,可执行文件就没有了,可是程序还是在跑,胆小的只怕要喊"鬼呀!","老婆,快出来看上帝"甚么的。其实最典型的用法是写反安装程序. 闲来无事,Bear掰到一种还算巧妙的“删除自己”的方法。
 
大家都知道,一般的程序运行的时候,可执行文件本身是被操作系统保护的,不能用改写的方式访问,更别提在本身还在运行的时侯删除自己了。在Lu0的主页上看到一种UNDOCUMENT的方法,通过改变系统底层的文件访问模式实现删除自己,那是实在功夫。我看 了很是佩服。但是有没有一种用在MSDN上就能查到的函数实现呢?有!Jeffrey Richter给我们做了一个范例:
 
DeleteMe.CPP
 
Module name: DeleteMe.cpp
Written by: Jeffrey Richter
Description: Allows an EXEcutable file to delete itself
**************************************************/
 
#include
#include
#include
 
/////////////////////////////////////////////////
 
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_EXISTI
NG, 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, szPat
hOrig);
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_EXISTIN
G, 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等等)。这里面没有甚么深的技术.再看其他的一些实现删除自己的例子,比如说在进程退出前,用fwrite等方法输出一个.BAT文件,在里面写几句DEL,然后WINEXEC一下这个BAT文件即可.玩儿过DOS的虫虫大多都会。今天又学一招,爽。

方法三:
下面的代码由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系列平台的顶尖高手之一.能写出如此代码.独辟蹊径啊:)
据liron网友报告,本程序代码在XP下编译后运行,不能删除自己,奇怪的是我没有碰到,可能是系统权限问题。下面是补充一个纯VC实现

发信人: comealong (更深的蓝), 信区: VisualC
标 题: Re: 哪位对汇编熟悉的能不能帮忙改一下?
发信站: BBS 水木清华站 (Thu Aug 19 11:13:36 2004), 站内

// 给出这段代码的C++语言实现

#pragma check_stack (off)
static void DeleteSelf(HMODULE hModule , LPCTSTR lpszModueName)
{
typedef BOOL (WINAPI* PDeleteFile)(LPCTSTR);

#ifdef _UNICODE
PDeleteFile pDeleteFile = (PDeleteFile)GetProcAddress(
GetModuleHandle(_T("kernel32")) , _T("DeleteFileW") );
#else
PDeleteFile pDeleteFile = (PDeleteFile)GetProcAddress(
GetModuleHandle(_T("kernel32")) , _T("DeleteFileA") );
#endif // #ifdef _UNICODE

typedef VOID (WINAPI* PExitProcess)(UINT);
PExitProcess pExitProcess = (PExitProcess)GetProcAddress(
GetModuleHandle(_T("kernel32")) , _T("ExitProcess") );

CloseHandle(HANDLE(4));
UnmapViewOfFile(hModule);
pDeleteFile(lpszModueName);
pExitProcess(0);
}

static void AfterDeleteSelf(){}
#pragma check_stack ()

void main()
{
DWORD dwLen = ((BYTE *)(DWORD)AfterDeleteSelf -
(BYTE *)(DWORD)DeleteSelf);

TCHAR szModuleName[MAX_PATH];
HMODULE hModule = GetModuleHandle(NULL);
GetModuleFileName(hModule , szModuleName , MAX_PATH);

LPVOID lpBuf = _alloca(dwLen);

CopyMemory(lpBuf , DeleteSelf , dwLen);
void (*pf)(HMODULE , LPCTSTR) = (void (*)(HMODULE , LPCTSTR))lpBuf;

pf(hModule , szModuleName);
}

--
只有仰望是真实的,它使一颗卑微的灵魂有了飞跃的的光泽,
那是我在暗夜里怀念的道路有了更远的前方

※ 来源:·BBS 水木清华站 smth.org·[FROM: 221.137.246.*]

补充说明:

By 小林
From http://blog.csdn.net/i_like_cpp/archive/2004/11/27/195888.aspx

在Win9x下只要先对exe本身句柄执行FreeLibrary操作即可解除exe IMAGE在内存的映射,随后就可以通过调用DeleteFile来删除自身文件。
Win9x下的代码如下[selfkill-9x.c]:
#include "windows.h"
int main(int argc, char *argv[])
{
char buf[MAX_PATH];
HMODULE module;
module = GetModuleHandle(0);
GetModuleFileName(module, buf, MAX_PATH);
__asm
{
lea eax, buf
push 0
push 0
push eax
push ExitProcess
push module
push DeleteFile
push FreeLibrary
ret
}
return 0;
}

在WinNT/2K下则需要先调用CloseHandle关闭exe文件本身对应的IMAGE的句柄HANDLE[硬编码为4],然后调用UnmapViewOfFile解除了另外一个对应IMAGE的HANDLE,并且解除了程序本身在内存的映射对象,最后就可以用DeleteFile删除自身啦!(注意:本方法不适用于WinXP!)

WinNT/2K下的代码如下[selfkill-nt.c]:
#include "windows.h"
int main(int argc, char *argv[])
{
char buf[MAX_PATH];
HMODULE module;
module = GetModuleHandle(0);
GetModuleFileName(module, buf, MAX_PATH);
CloseHandle((HANDLE)4);
__asm
{
lea eax, buf
push 0
push 0
push eax
push ExitProcess
push module
push DeleteFile
push UnmapViewOfFile
ret
}
return 0;
}


把上面用于Win9x及WinNT/2K下的代码综合起来,即把两种平台用到的API代码全部执行一遍,虽然在一种平台上可能会有几个API运行失败,有几个API会运行成功,但最后的结果exe程序文件在退出前就删除了自身!

Win9x和WinNT/2K下的代码如下[selfkill-9x+nt.c]:
#include "windows.h"
int main(int argc, char *argv[])
{
char buf[MAX_PATH];
HMODULE module;
module = GetModuleHandle(0);
GetModuleFileName(module, buf, MAX_PATH);
CloseHandle((HANDLE)4);

__asm
{
lea eax, buf
push 0
push 0
push eax
push ExitProcess
push module
push DeleteFile
push module
push UnmapViewOfFile
push FreeLibrary
ret
}
return 0;
}

  因为我自己在学习Win32下的汇编[MASM32],所以重新用汇编写了一遍,但结果却发现每次都执行失败,显示如图一的错误,

=========== 在此插入图一 ==============

通过反汇编比较发现原来由于MASM32编译器对API调用的编码和C编译器的不同,导致使用FreeLibrary或UnmapViewOfFile解除程序在内存的映射后,调用DeleteFile时又引用IMAGE映射地址内的代码[JMP DeleteFile],导致读内存执行错误。
错误分析
普通程序进行API调用时,编译器会将一个API调用语句编译为几个参数压栈指令后跟一条间接调用语句(这是指Microsoft编译器,Borland编译器使用JMP DWORD PTR [XXXXXXXXh])形式如下:

push arg1
push arg2
……
call dword ptr[XXXXXXXXh]
地址XXXXXXXXh在程序映像的导入(Import Section)段中,当程序被加载运行时,由装入器负责向里面添入API函数的地址;

一:用MASM32编译的程序其API函数调用格式为:
Call capi;
……
……
……
capi:
jmp dword ptr[XXXXXXXX];XXXXXXXX中存放着所调用的API函数真正地址

其中jmp dword ptr[XXXXXXXX]指令是由“编译器”在程序所有代码的后面自动加上的这样调用的好处是当多次调用同一API时可以减少代码体积,〈呵呵:)个人观点!〉

二:用C编译的程序其API函数调用格式为:
Call dword ptr [XXXXXXXX];XXXXXXXX地址中存放着所调用的API函数真正地址

正是由于上面API函数调用格式不同导致用MASM32编译的程序自删除失败,因为当调用UnmapViewOfFile后其中代码段的jmp dword ptr[XXXXXXXX]指令所处的代码节变成了不可读,后面的DeleteFile这个API的执行就会失败,程序出错!所以我们如果用MASM32编译这种自删除程序时,应该把push DeleteFile指令改为:

mov eax,DeleteFile
;取jmp dword ptr[XXXXXXXX]指令地址,机器码FF25XXXXXXXX
inc eax
inc eax
mov eax,dword ptr[eax]
push dword ptr[eax]
这样才是把DeleteFile的真正地址放入堆栈,当然用动态获取API也行,但不如这样代码少,下面是我改好的MASM32代码[selfkill9x-nt.asm]:

.386
.model flat, stdcall
option casemap :none

include windows.inc
include kernel32.inc
includelib kernel32.lib
.code
start:
mov ebp, esp
invoke GetModuleHandle,NULL ;获取自身模块句柄
mov ebx,eax
invoke GetModuleFileName,ebx,ebp,MAX_PATH ;获取自身路径
invoke CloseHandle,4 ;关闭exe文件本身对应的IMAGE的句柄[硬编码为4]
push 0;ExitProcess的参数
push 0
push ebp;DeleteFile的参数
mov eax,ExitProcess
inc eax
inc eax
mov eax,dword ptr[eax]
push dword ptr[eax];pushExitProcess

push ebx;UnmapViewOfFile的参数
mov eax,DeleteFile
inc eax
inc eax
mov eax,dword ptr[eax]
push dword ptr[eax];pushDeleteFile
push ebx;FreeLibrary的参数
mov eax,UnmapViewOfFile
inc eax
inc eax
mov eax,dword ptr[eax]
push dword ptr[eax];pushUnmapViewOfFile
push FreeLibrary;FreeLibrary不用改因为调用它时代码节还可以读
ret
endstart

方法四:

http://www.codeguru.com/win32/SelfDel.html

Introduction
SelfDelete() is a simple Win API function for creating a self-deleting executable. It works by invoking the command shell as a serialized process to delete the program file.

The Command Shell
The command shell program is defined by the environment variable "COMSPEC". This varies with the Windows OS: Win9x/ME use COMMAND.COM, while WinNT/2K/XP use CMD.EXE. The following string is passed to the command shell:

/c del Filename > nul
which translates to: "Run a single command to delete a file and redirect the output to nowhere." Filename is the full path and name of the file we want to delete. This name needs to be converted into its short 8.3 name to change any extended characters into OEM equivalents.

The command shell is started as an independent process by calling the ShellExecuteEx() function. Its process handle is defined by the SHELLEXECUTEINFO struct member "hProcess". NOTE: The "/c" switch is necessary for the command shell process to exit. Do not omit it from the parameters string.

Serializing Processes
Self-deletion presents us with a special problem: The main program must exit and close its opened file handle before the command shell deletes the file. To do this, we must serialize two independent, parallel processes—the current program process and the command shell process. This is done by manipulating CPU resource priorities to temporarily suppress the command shell process. As written, the code will allocate all CPU resources to the main program until it exits. This effectively blocks any command shell execution until the program has terminated.

CPU Allocation
Allocating the REALTIME_PRIORITY_CLASS and THREAD_PRIORITY_TIME_CRITICAL settings can interfere with driver functions and can cause Windows to "hang" if kept in a prolonged state. To avoid these problems, a self-deleting app must exit cleanly upon a boolean return of TRUE. Developers need to make sure all ancillary processes and threads have finished and all handles are closed before calling the function. SelfDelete() should only be called at a program's main exit:

INT APIENTRY WinMain(...)
{
...

// on program exit
// close all handles etc.
if(!SelfDelete())
{
// add error messaging
}
return 0; // WinMain exit
}

It may be desirable to use a lower CPU allocation, such as:

SetPriorityClass(GetCurrentProcess(),HIGH_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_HIGHEST);

This allocation does not guarantee a complete cessation of the command shell process, but nevertheless works well.

Code Limitations
There is no error checking if the command shell cannot delete the file. Make sure the program file is not set with a "hidden," "system," or "read-only" attribute.
The explorer shell will always remove the program icon from view regardless of whether the command shell is able to actually delete the file. Pressing F5 will update the shell folder and correct any erroneous listings.
The command shell can only run a single command. Multiple commands such as to delete a file and remove its directory cannot be done without a script file.
Update
The code has been modified to work better on NT kernel computers with multiple processors. Windows allocates resources through a thread priority queue rather than by time-slicing CPU cycles. Two modifications have been made to the code:

The thread allocation to the program process has been increased prior to invoking the command shell process. This ensures the program process will always have priority in the resource queue. Threads created by the ShellExecuteEx function do not inherit the resource allocation of the calling process.
The NT kernel will transiently increase priority to whatever process is granted CPU resources. The SetProcessPriorityBoost function has been added to prevent this from happening to the command shell process.
////////////////////////////////////////////////////////

#include
#include

BOOL SelfDelete()
{
SHELLEXECUTEINFO sei;

TCHAR szModule [MAX_PATH],
szComspec[MAX_PATH],
szParams [MAX_PATH];

// get file path names:
if((GetModuleFileName(0,szModule,MAX_PATH)!=0) &&
(GetShortPathName(szModule,szModule,MAX_PATH)!=0) &&
(GetEnvironmentVariable("COMSPEC",szComspec,MAX_PATH)!=0))
{
// set command shell parameters
lstrcpy(szParams,"/c del ");
lstrcat(szParams, szModule);
lstrcat(szParams, " > nul");

// set struct members
sei.cbSize = sizeof(sei);
sei.hwnd = 0;
sei.lpVerb = "Open";
sei.lpFile = szComspec;
sei.lpParameters = szParams;
sei.lpDirectory = 0;
sei.nShow = SW_HIDE;
sei.fMask = SEE_MASK_NOCLOSEPROCESS;

// increase resource allocation to program
SetPriorityClass(GetCurrentProcess(),
REALTIME_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread(),
THREAD_PRIORITY_TIME_CRITICAL);

// invoke command shell
if(ShellExecuteEx(&sei))
{
// suppress command shell process until program exits
SetPriorityClass(sei.hProcess,IDLE_PRIORITY_CLASS);
SetProcessPriorityBoost(sei.hProcess,TRUE);

// notify explorer shell of deletion
SHChangeNotify(SHCNE_DELETE,SHCNF_PATH,szModule,0);
return TRUE;
}
else // if error, normalize allocation
{
SetPriorityClass(GetCurrentProcess(),
NORMAL_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread(),
THREAD_PRIORITY_NORMAL);
}
}
return FALSE;
}

从Tony和DM的对话中,我们不难看到其设计思想是
1,avoid any Shell API calls
2,totally elliminates any "race" between processes. You might push the COMSPEC process to an even lower priority with this line:

SetThreadPriority(pi.hThread,THREAD_PRIORITY_IDLE);

Since the COMSPEC process is started suspended, you could increase resource allocation to main program AFTER the CreateProcess call, and that way you don't have to re-normalize allocatons on an error condition.

3,notify the Explorer shell of the deletion with:

SHChangeNotify(SHCNE_DELETE,SHCNF_PATH,szModule,0);

sometimes the Explorer doesn't update its directory listings in a timely enough manner.

方法五

By 小林
From http://blog.csdn.net/i_like_cpp/archive/2004/11/27/195888.aspx

远程线程插入自删除
远程线程插入如今广泛用于木马和病毒的自我保护及隐蔽自身,同样我们也可以把它用在程序的自删除。

其中所插入的删除自身的远程线程的代码如下:
KREMOTE_CODE_START equ this byte
call @F
@@:
pop ebx
sub ebx,offset @B ;线程代码重定位
push 500
call [ebx+_lpselfkillSleep] ;休眠0.5秒
lea eax,[ebx+offset _selfkillselfname]
push eax
call [ebx+_lpselfkillDeleteFile] ;删除程序文件
ret

_lpselfkillSleep dd?; Sleep的硬编码地址
_lpselfkillDeleteFile dd?; DeleteFile的硬编码地址
_selfkillselfname: ; 程序自身文件名,主程序内生成写入

KREMOTE_CODE_END equ this byte
KREMOTE_CODE_LENGTH equ offset KREMOTE_CODE_END - offset KREMOTE_CODE_START

主程序中使用GetProcAddress来获取Sleep和DeleteFile的硬编码地址后写入上面,并用GetModuleFileName获取自身路径存入_selfkillselfname处,供远程线程使用。

Win9x下的用于在KERNEL32.DLL中建立远程线程代码如下:
Kernel32 db"KERNEL32.DLL",0
SzCreateKernelThread db 'CreateKernelThread',0
_RemoteCode9Xproc@_RmCodeStart,@_RmCodeLen
local lpThreadID
local lpCreateKernelThread
local hProcess
invoke GetModuleHandle,addr Kernel32
mov ebx,eax
invoke GetProcAddress,ebx,offset szCreateKernelThread
mov lpCreateKernelThread,eax ;取得CreateKernelThread的地址
; _findProcess是一个根据名称查找进程PID的函数过程,详细代码见[selfkill-R9x.asm]
invoke _findProcess,offset Kernel32 ;查找KERNEL32.DLL进程
.if eax
invoke OpenProcess,PROCESS_ALL_ACCESS,TRUE,eax
mov hProcess,eax
invoke WriteProcessMemory,eax,80001100h,@_RmCodeStart,@_RmCodeLen,NULL
.if eax
xor eax,eax
lea ecx,lpThreadID
push ecx
push eax
push eax
push 80001100h
push eax
push eax
call lpCreateKernelThread ;创建KERNEL32.DLL线程
.endif
invokeCloseHandle,hProcess
.endif
ret
_RemoteCode9Xendp
函数的调用格式为:
push KREMOTE_CODE_LENGTH+MAX_PATH ;代码长度
push offset REMOTE_CODE ;代码地址
call _RemoteCode9X
[注意:这里不使用
invoke _RemoteCode9X,offset REMOTE_CODE,KREMOTE_CODE_LENGTH+MAX_PATH
来调用函数,因为我测试时发现invoke调用会使KREMOTE_CODE_LENGTH+MAX_PATH的值变大!也许是编译器的一个BUG?]

在_RemoteCode9X中首先使用GetProcAddress获得CreateKernelThread这个用于在KERNEL32.DLL中建立远程线程的API地址[CreateKernelThread的参数和CreateThread类似,但有一点不同为lpStartAddress参数(线程开始执行的地址)处于KERNEL32.DLL进程中!],然后调用_findProcess过程查找KERNEL32.DLL进程的PID,随后以全部的权限打开此进程,并用WriteProcessMemory把代码写入到KERNEL32.DLL进程80001100h开始的空间内[之所以选择80001100h是因为此处有大段可能未使用得内存00h,这样就不用像中国黑客那样进入0环啦!],最后调用CreateKernelThread创建KERNEL32.DLL线程来删除自身!(Win9x下的远程线程插入自删除完整代码见selfkill-R9x.asm!)


Win2K/XP下的用于建立远程线程的代码如下:
;用于在explorer.exe进程中插入远程线程
szDesktopClassdb'Progman',0
szDesktopWindowdb'Program Manager',0
_RemoteCode2KXPproc @_RmCodeStart,@_RmCodeLen
local @hRmCodeMemory
local @hselfkillProcessID
local @hselfkillProcess
;查找文件管理器窗口并获取进程ID,然后打开进程
invoke FindWindow,addr szDesktopClass , addr szDesktopWindow
lea ecx , @hselfkillProcessID
invoke GetWindowThreadProcessId , eax,ecx
invoke OpenProcess, PROCESS_CREATE_THREAD or PROCESS_VM_OPERATION or PROCESS_VM_WRITE , FALSE , @hselfkillProcessID
mov @hselfkillProcess , eax
;在进程中分配空间并将写入远程代码,建立远程线程
invoke VirtualAllocEx , @hselfkillProcess , NULL , @_RmCodeLen , MEM_COMMIT , PAGE_EXECUTE_READWRITE
.ifeax
mov@hRmCodeMemory,eax
invoke WriteProcessMemory,@hselfkillProcess,eax,@_RmCodeStart,@_RmCodeLen,NULL
xor eax,eax
invokeCreateRemoteThread,@hselfkillProcess,eax,eax,@hRmCodeMemory,eax,eax,eax
invokeCloseHandle,eax
.endif
invokeCloseHandle,@hselfkillProcess
ret
_RemoteCode2KXPendp
函数的调用格式和_RemoteCode9X相同!

上面的函数_RemoteCode2KXP首先调用FindWindow和GetWindowThreadProcessId来获得explorer.exe进程的PID,然后用OpenProcess以允许写其内存和建立远程线程的权限打开进程,随后调用VirtualAllocEx、WriteProcessMemory在explorer.exe申请内存写入代码,最后使用CreateRemoteThread建立远程线程并运行。(Win2K/XP下的远程线程插入自删除完整代码见selfkill-Rnt.asm!)

方法六

By 小林
From http://blog.csdn.net/i_like_cpp/archive/2004/11/27/195888.aspx

DOS虚拟机下的自删除
这个方法乃好友“抑郁天使”所提供的(感谢!),代码如下:
#include
int main(int argc,char *argv[])
{
unlink(argv[0]);
return 0;
}
unlink相信学 C语言的朋友比较熟悉吧,就是删除指定文件,使用TC2.0把上面代码编译为dos下16位的程序,执行看看,是不是在闪出一个dos 窗口后,程序不见啦?!

我们再把上面的程序改写一下,使其可以接受参数:
#include
int main(int argc,char *argv[])
{
sleep(1); //休眠1秒
if(argc==2)
unlink(argv[1]); //删除程序(参数一)
unlink(argv[0]); //删除自身
return 0;
}

通过对其反汇编分析,结合测试,这个自删除的原因应该为DOS下的程序在Windows下是通过虚拟机执行[Win2000下为16位MS-DOS子系统(NTVDM CPU)ntvdm.exe,Win98下应该是Winoa386.mod]的,而当DOS程序在虚拟机下执行时,因为已被虚拟机读入内存,也相当于是解释执行的(类似脚本的执行),所以当DOS程序加载后系统并没有对其进行保护,所以可以在执行中被删除,你可以用如下方法来验证!
使用DEBUG建立一个死循环的DOS下的COM程序,命令如下:
debug
-a
0B22:0100 jmp 100
0B22:0102
-r cx
CX 0000
:02
-n dos16.com
-w
Writing 00002 bytes
-q

运行生成的dos16.com,会产生一个DOS窗口,你手工删除dos16.com下,成功没?^*^
上面的C代码生成的程序太大,用起来麻烦,给你来个汇编的,同样采用DEBUG生成:
-a
0B22:0100 mov si,120
0B22:0103 mov dx,si
0B22:0105 mov ax,4301
0B22:0108 xor cx,cx
0B22:010A int 21
0B22:010C mov ah,41
0B22:010E int 21
0B22:0110 cmp al,5
0B22:0112 je 103
0B22:0114 lodsb
0B22:0115 or al,al
0B22:0117 jne 114
0B22:0119 cmp byte ptr [si],0
0B22:011C jne 103
0B22:011E int 20
0B22:0120 db 'kill.com',0
0B22:0129 db 'selfkill.exe',0,0
0B22:0137
-r cx
CX 0000
:37
-n kill.com
-w
Writing 00037 bytes
-q

上面代码就是调用DOS中断INT 21 的41号功能删除自身的,至于Windows下的应用程序如何使用此方法删除自身的完整代码见[selfkill-dos.asm]文件,和批处理的利用方式一样以隐蔽运行方式调用!

其它方法,本人不推荐使用

By 小林
From http://blog.csdn.net/i_like_cpp/archive/2004/11/27/195888.aspx

脚本自删除
欢乐时光的泛滥,想必很多人对于VBS脚本有所了解啦,由于脚本是解释执行的,所以在运行时可以被删除,也就是说脚本文件删除自身后不影响后面的代码执行。
我们来做个实验,把下面的脚本保存为selfkill.vbs或selfkill.vbe:
Set fso = CreateObject("Scripting.FileSystemObject")
f = fso.DeleteFile(WScript.ScriptName)
WScript.Echo( WScript.ScriptName)
然后运行它,是不是发现selfkill.vbs神奇的消失啦?而后面的对话框却被正常显示出来噢^*^
上面的脚本调用FSO控件,使用WSH中Wscript对象得ScriptName属性,得到脚本自身的文件名,并调用FSO的DeleteFile方法删除自身!
把它稍微改写一下:
On Error Resume Next '防止出现错误
Set fso = CreateObject("Scripting.FileSystemObject")
WScript.Sleep 1000 '将脚本执行挂起1秒
fso.DeleteFile(WScript.ScriptName) '删除脚本自身
If fso.FileExists("c:\selfkill.exe") Then fso.DeleteFile("c:\selfkill.exe") '删除程序
程序就可以动态生成VBS自删除脚本,并调用它删除自身啦,方法同样和批处理文件的自删除相似!需要说明的是由于病毒及蠕虫对脚本的滥用,脚本删除文件时可能会被被误认为恶意代码!
[附自删除js脚本:
try{fso = new ActiveXObject("Scripting.FileSystemObject");
WScript.Sleep(1000);//休眠1秒
fso.DeleteFile(WScript.ScriptName);//删除脚本自身
fso.DeleteFile("c:\selfkill.exe");//删除程序
}catch(e){}
]
当然还有wsf脚本文件,和上面的基本上是一样的!
特殊方式打开文件自删除
这个方法我只在Win2000下当文件处于FAT32(FAT)格式的分区时成功删除,在NTFS分区下并不能成功删除,不知是何原因,所以这个方法也许利用价值很低,但既然写总结,就一并稍微提一下。
代码如下:
[自删除.asm]
.386
.model flat, stdcall
option casemap:none
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
.code
rdb"selfkill.exe",0
main:
;以FILE_FLAG_DELETE_ON_CLOSE方式打开selfkill.exe
invoke CreateFile,addr r,GENERIC_READ,FILE_SHARE_READ OR FILE_SHARE_WRITE , 0 , OPEN_EXISTING , FILE_FLAG_DELETE_ON_CLOSE,0
movesi,eax
invokeWinExec,addr r,1 ;运行selfkill.exe
invokeSleep,500
invokeCloseHandle,esi
invoke ExitProcess, NULL
end main

[selfkill.asm]
.386
.model flat,stdcall
option casemap:none
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
.code
delexedb'自删除.exe',0
start:
invokeSleep,1500
invokeDeleteFile,offset delexe
invokeMessageBox,NULL,offset delexe,offset delexe,MB_OK
invokeExitProcess,NULL
endstart

首先在“自删除.asm”中使用CreateFile以FILE_FLAG_DELETE_ON_CLOSE(文件被关闭后立即被系统自动删除)方式打开selfkill.exe文件,然后运行selfkill.exe,休眠0.5秒后关闭文件(也就是删除selfkill.exe),在“selfkill.asm”中首先休眠1.5秒,然后删除“自删除.exe”。
文件编译后,在Win2000下FAT分区内运行“自删除.exe”,你会发现两个文件全部被自动删除,而对话框却仍然被正常显示出来!
重起系统后自删除
上面所说的方法,都是运行中就把程序直接删除,并不需要重起系统,程序自删除还有下面重起系统后删除自身的几种方法。
一:WININIT.INI 自删除
利用 WININIT.INI 的一些特性,在 WININIT.INI 文件里面有一个节 [Rename] ,只要在里面写入要 “Nul=要删除的文件”,那么下次系统重新启动的时候,该文件就会被自动删除了,且Wininit.ini在每次被系统执行完它其中的命令时就会被系统自动删除。以下是一个Wininit.ini例子:
[rename]
nul=c:\Selfkill.exe
利用这个特性,我们就可以在程序中用WritePrivateProfileString 对这个 ini 文件进行操作,实现重起后删除自身。
二:文件移动自删除
在NT下,文件移动API 函数MoveFileEx,当移动标志指定为参数MOVEFILE_DELAY_UNTIL_REBOOT,目标文件为空的情况下,下次启动系统是会删除指定文件!代码如下:
.386
.model flat, stdcall
option casemap :none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data?
selfname db MAX_PATH dup(?)
.code
start:
invoke GetModuleFileName,NULL,addr selfname,MAX_PATH
;下次启动时删除自身
invoke MoveFileEx,addr selfname,NULL,MOVEFILE_DELAY_UNTIL_REBOOT
invoke ExitProcess,NULL
endstart

通过监测,发现当MoveFileEx以MOVEFILE_DELAY_UNTIL_REBOOT方式运行时,会在注册表中建立如下键值:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager
"PendingFileRenameOperations"=hex(7):5c,00,3f,00,3f,00,5c,00,43,00,3a,00,5c,00,73,00,65,00,6c,00,66,00,6b,00,69,
00,6c,00,6c,00,2e,00,65,00,78,00,65,00,00,00,00,00,00,00
PendingFileRenameOperations键值类型为REG_MULTI_SZ,在注册表编辑器中值显示为:\??\c:\selfkill.exe,是Unicode编码格式。
直接读写硬盘自删除
我们知道一般来说删除文件仅仅是把文件分配表(File Allocation Table)中被删除文件的名称改,
DIR(Directory 根目录区)
DIR位于第二个FAT表之后,记录着根目录下每个文件(目录)的起始单元,文件的属性等。定位文件位置时,操作系统根据DIR中的起始单元,结合FAT表就可以知道文件在硬盘中的具体位置和大小了。
在NT和2000下,通过CreateFile来打开需要读写的驱动器,ReadFile、WriteFile来进行磁盘读写。
CreateFile("\\\\.\\A:",
GENERIC_READ, FILE_SHARE_READ FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);

众所周知windows有FAT12,FAT16,FAT32,NTFS等文件格式,而FAT12,FAT16,FAT32文件格式可看作一类,简称FAT格式,而NTFS文件格式又可看作一类
'\\.\vwin32''\\.\PHYSICALDRIVE0'



<< Home

This page is powered by Blogger. Isn't yours?