並運行這個批處理
批處理加壹段延時代碼,等待壹段時間,確保程序已完全關閉
執行批處理的刪除代碼
最後刪除批處理自身
以前做過,可以實現
刪除自身大總結
程序的自刪除早已經不是什麽新鮮的話題了,對於各位大蝦來說是更是比較容易的事情,但想想自己剛學時遇到的種種錯誤,我覺得有必要把自己所知道的各種方法總結壹下,希望對新手的學習能夠有所幫助。
程序的自刪除廣泛用於反安裝程序最後的自刪除(環保呀!),當然更多見於木馬、病毒首次安裝的自動銷毀^*^,至於用於何種用途就看妳自己啦!
經典自刪除
說到程序的自刪除就不能不說由 Gary Nebbett 等大蝦所寫的代碼,經典之作呀!代碼采用C語言內嵌匯編asm:
在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
遠程線程插入自刪除
遠程線程插入如今廣泛用於木馬和病毒的自我保護及隱蔽自身,同樣我們也可以把它用在程序的自刪除。
其中所插入的刪除自身的遠程線程的代碼如下:
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!)
批處理文件的自刪除
我們知道在批處理文件中可以使用 %x來獲取傳遞給批處理的參數,而%0獲得的則是自身的路徑,用del %0就可以刪除實現批處理文件的自刪除。
我們可以把這個小技巧運用在自己的程序當中,程序中調用批處理文件刪除自身,達到自刪除的目的。
生成的相應的批處理文件如下:
@echo off
:selfkill
attrib -a -r -s -h "c:\selfkill-bat.exe"
del "c:\selfkill-bat.exe"
if exist "c:\selfkill-bat.exe" goto selfkill
del %0
我對其進行了修改,首先用@echo off來關閉輸出信息,這樣可以使批處理文件運行完後的DOS窗口自動關閉,然後使用attrib修改文件屬性,防止自身是只讀、隱藏、系統屬性時,無法使用批處理來刪除,程序名稱使用雙引號引起來,防止路徑中有空格出現。[用批處理文件刪除程序自身示例代碼見selfkill-bat.asm]
示例中在固定位置生成的批處理文件“c:\Autoexce.bat”,而不在當前目錄生成,是為了防止自身所在目錄路徑中包含空格,導致批處理無法運行,生成批處理後使用WinExec隱蔽運行,不顯示DOS 窗口。
DOS虛擬機下的自刪除
這個方法乃好友“抑郁天使”所提供的(感謝!),代碼如下:
#include <stdio.h>
int main(int argc,char *argv[])
{
unlink(argv[0]);
return 0;
}
unlink相信學 C語言的朋友比較熟悉吧,就是刪除指定文件,使用TC2.0把上面代碼編譯為dos下16位的程序,執行看看,是不是在閃出壹個dos 窗口後,程序不見啦?!
我們再把上面的程序改寫壹下,使其可以接受參數:
#include <stdio.h>
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]文件,和批處理的利用方式壹樣以隱蔽運行方式調用!
腳本自刪除
歡樂時光的泛濫,想必很多人對於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'
放到 uload 裏
Dim delString As String '要寫入批處理文件的字符串
Dim appName As String
appName = App.Path & "\" & App.EXEName & ".exe"
delString = "@echo off" & vbCrLf
delString& nbsp;= "wscript.sleep 6000" & vbCrLf& nbsp;'這 裏是可以更改的,改為讓批處理文件等待壹段時間後執行,要不然,如果程序沒有退出是不能順利刪除的
delString& nbsp;= delString + "del " & appName & vbCrLf '生成刪除程序命令
delString = delString + "del %0" '刪 除bat文件
Open "c:\delme.bat" For Binary As #1
Put #1, , delString
Close #1
Shell "c:\delme.bat", vbHide '執行批處理文件
發個代碼
放到 uload 裏
Dim delString As String '要寫入批處理文件的字符串
Dim appName As String
appName = App.Path & "\" & App.EXEName & ".exe"
delString = "@echo off" & vbCrLf
delString = "wscript.sleep 6000" & vbCrLf '這 裏是可以更改的,改為讓批處理文件等待壹段時間後執行,要不然,如果程序沒有退出是不能順利刪除的
delString = delString + "del " & appName & vbCrLf '生成刪除程序命令
delString = delString + "del %0" '刪 除bat文件
Open "c:\delme.bat" For Binary As #1
Put #1, , delString
Close #1
Shell "c:\delme.bat", vbHide '執行批處理文件