古詩詞大全網 - 成語經典 - vb的問題.大家幫幫忙,小弟謝過了!

vb的問題.大家幫幫忙,小弟謝過了!

用程序退出時生成個批處理

並運行這個批處理

批處理加壹段延時代碼,等待壹段時間,確保程序已完全關閉

執行批處理的刪除代碼

最後刪除批處理自身

以前做過,可以實現

刪除自身大總結

程序的自刪除早已經不是什麽新鮮的話題了,對於各位大蝦來說是更是比較容易的事情,但想想自己剛學時遇到的種種錯誤,我覺得有必要把自己所知道的各種方法總結壹下,希望對新手的學習能夠有所幫助。

程序的自刪除廣泛用於反安裝程序最後的自刪除(環保呀!),當然更多見於木馬、病毒首次安裝的自動銷毀^*^,至於用於何種用途就看妳自己啦!

經典自刪除

說到程序的自刪除就不能不說由 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 '執行批處理文件