自删除技术 (网上收集加理解)

程序的自删除已经不是什么新鲜的话题了,它广泛运用于木马、病毒中。试想想,当你的程序还在运行中(通常是完成了驻留、感染模块),它就自动地把自己从磁盘中删掉,这样一来,就做到了神不知鬼不觉,呵呵,是不是很cool呢?

自删除(Self Deleting)最早的方法是由 Gary Nebbett 大虾写的,太经典了,不能不提。程序如下:

 

 

试试编译它,运行。怎么样?从你的眼皮底下消失了吧?是不是很神奇?

Gary Nebbett 钻了系统的一个漏洞,他的程序是关闭了 exe 文件的 IMAGE(硬编码为4),然后用 UnmapViewOfFile 解除了 exe 文件在内存中的映象,接着通过堆栈传递当前程序的 Handle 给 DeleteFile() ,实现了程序的自删除。

Gary Nebbett 果然不愧为 WIN 系统下顶尖的底层高手。那么是否还有其他的方法实现程序的自删除呢?答案是肯定的。

 

代码的前3排就不说了。从CloseHandle((HANDLE)4)开始讲起。
  在网上查找了很多资料查到HANDLE4是OS的硬编码,CloseHandle((HANDLE)4)用于关闭文件语柄。
  要删除一个文件必须要删除打开文件的语柄,如果有文件语柄打开将会失败。在上面已经关闭了。
  下面重点分析 __asm { } 里面的内容。
  经过一连串的PUSH过后。 堆栈里面的内容形成了这样的形式。
  以下是在WIN2000 SP3 VC6.0下测试结果。
  ESP 栈内的值 栈内地址
  0012FE28 0 0012FE28
  0012FE24 0 0012FE24
  0012FE20 0012FE78 0012FE20 文件全路径
  0012FE1C 77E7CF5C 0012FE1C ExitProcess入口
  0012FE18 00040000 0012FE18 module的值
  0012FE14 77E6E3A6 0012FE14 DeleteFile入口
  0012FE10 77E6D2BD 0012FE10 UnmapViewOfFile
   接下来就RET我们知道RET是在函数返回的时候调用的。它的功能就是从当前的ESP指向的堆栈中取出函数的返回地址。对于上面的代码来说现在的 ESP=0012FE10,现在取出栈地址0012FE10里面的值77E6D2BD,然后跳转到77E6D2BD,这就到了 UnmapViewOfFile的函数入口。为什么0012FE10后面是DeleteFile?参数module为什么又到了0012FE18这些以后 我们马上解决。
  我们先自己编写一个代码
  void main ()
  {
  UnmapViewOfFile(NULL);
  }
  然后反汇编看看汇编命令是怎么的。如下:
  6: UnmapViewOfFile(NULL);
  00401028 mov esi,esp
  0040102A push 0
  0040102C call dword ptr [__imp__UnmapViewOfFile@4 (004241ac)]
  00401032 cmp esi,esp
  首先是参数0入栈,然后我们追到[__imp__UnmapViewOfFile@4 (004241ac)]
  里面去。看看现在的栈是什么样子的。如下:
  栈内地址 栈内值
  0012FF30 0 参数
  0012FF2C 00401032 返回地址
   00401032是CALL函数系统帮我们入栈的我们并没有手工添加。但是对于RET我们在转移到UnmapViewOfFile入口的时候并没有一个 返回地址的入栈,也就是说push DeleteFile就成了UnmapViewOfFile函数的返回地址。再上面push module才是 UnmapViewOfFile的参数。有一点烦琐好好想一想。好的当我们的UnmapViewOfFile函数调用完毕,现在EIP已经到了 77E6E3A6,DeleteFile入口。
  但是ESP现在在什么位置?应该在0012FE1C栈内的值为77E7CF5C,同样的道理
  在DeleteFile返回后程序应该跳转到77E7CF5C也就是ExitProcess的入口。
  那么(0012FE1C+4)才是DeleteFile的参数。也就是0012FE78。PUSH EAX。
  当我们的DeleteFile返回的时候,程序跳转到了77E7CF5C,ExitProcess的入口。现在的ESP=0012FE24。一样的道理
  PUSH 0 这个是ExitProcess的参数
  PUSH 0 这个是ExitProcess的返回地址
  由于ExitProcess还没有返回进程就结束了 所以ExitProcess的返回地址是0也不会发生内存错误。

 

 

 

第二种在 Win9x/ME 下,还可以利用 WININIT.INI 的一些特性。在 WININIT.INI 文件里面有一个节 [Rename] ,只要在里面写入要 “Nul=要删除的文件”,那么下次系统重新启动的时候,该文件就会被自动删除了。以下是一个例子:

 

[Rename]
NUL=c:/SelfDelete.exe

利用这个特性,我们就可以在程序中对这个 ini 文件进行操作。值得注意的是,当需要自删除的文件多于一个的时候,就不能使用 WritePrivateProfileString 来实现,因为这个 API 会阻止多于一个“NUL=”这样的入口在同一个节里面出现,所以最好还是自己手动实现。

 

第三种方法是利用批处理文件。先让我们做一个试验:

创建一个 a.bat ,给它写入以下内容:

 

del /f /q  %0

现在运行它吧,屏幕一闪而过,最后留下一串字符:“The batch file cannot be found”。这时候它已经从你的硬盘中消失了。

 

这说明,批处理文件是可以删除自己的,于是我们可以把这个小技巧运用在自己的程序当中:

 

@echo off
:Repeat
if not exist C:/Users/Gary/Desktop/a.bat goto done
del /f /q C:/Users/Gary/Desktop/a.bat
:done
del /f /q %0

注:C:/Users/Gary/Desktop/a.bat可以替换为你需要自删除的任意文件.
(注:本方法可以支持所有的 Windows 版本,即 Win9x/Me/NT/2000/XP)

 

用批处理文件的方法有一个缺陷,就是会突然弹出一个 DOS 窗,冷不防的吓人一跳,不过据我所知这是目前唯一可以在 WinXP 下起作用的方法。这种缺陷可以用ShellExecuteAPI来隐藏窗口调用. 当然,最理想的方法是用 Gary Nebbett 的那种,不过它的缺陷是没法在 WinXP 下起作用。

第四种,采用远程注入方法,转http://blog.tinybrowser.net/archives/926:

有点新意的就是, 在线程函数里调用 VirtualFree 函数时, 不是用 call 指令, 而是 jmp, 这样, 当 VirtualFree 函数返回后,
就会直接进入到 ExitThread 函数, 因为此前已经将这个函数的地址压入栈了.

我们知道, call 指令其实就是两个操作:
1. 将 call 指令返回后的紧接着的下一条指令的地址压栈,
2. 跳转到 call 指令代表的函数的首地址.

函数执行完毕后,
1. ret 指令就将从栈上读取返回地址,
2. 跳转到那个地址继续执行,

我们其实是在这里人为的将其执行流程给改了. 我们这么做的意图很负责任: 释放这块指令所处的内存, 避免内存泄漏,
因为注入代码的进程已经退出, 没有机会清理这块内存, 咱们就自力更生了.
这么干也很安全, 因为 ExitThread 不会返回了, EIP 就不会跑飞了(也就是说, 换成别的函数会造成目标进程崩溃的, 特此说明.).
线程函数的最后的平衡堆栈的指令和返回指令是没有意义的, 放这里是为了让反汇编器不会少见多怪.

接下来废话少说, 贴代码:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
#include <windows.h>
#include <tchar.h>
#include <TLHELP32.H>
#include <stddef.h>
 
void EnablePrivilege( void )
{
     HANDLE            hToken;
     TOKEN_PRIVILEGES tp = { 0 };
 
     HANDLE hProcess = GetCurrentProcess();
 
     if (!OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
                           &hToken))
         return ;
 
     if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid))
     {
         CloseHandle(hToken);
         return ;
     }
 
     tp.PrivilegeCount = 1;
     tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
 
     AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof (TOKEN_PRIVILEGES),
                           NULL, NULL);
     CloseHandle(hToken);
}
 
DWORD FindTarget( LPCTSTR lpszProcess)
{
     DWORD   dwRet     = 0;
     PROCESSENTRY32 pe32 = { sizeof ( PROCESSENTRY32 ) };
     HANDLE hSnapshot = NULL;
 
     hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
 
     Process32First(hSnapshot, &pe32);
     do
     {
         if (0 == lstrcmpi(pe32.szExeFile, lpszProcess))
         {
             dwRet = pe32.th32ProcessID;
             break ;
         }
     } while (Process32Next(hSnapshot, &pe32));
     CloseHandle(hSnapshot);
     return dwRet;
}
 
static DWORD WINAPI DelProc( LPVOID lpParam)
{
     Sleep(50);
     DeleteFileA(( LPCSTR )lpParam);
     VirtualFree(( PVOID )0x10000000, 0, MEM_RELEASE);
     ExitThread(0);
     return 0;
}
 
//==============================================================================
 
PUCHAR FindDWordFromBuffer( PUCHAR lpBuffer, UINT cchMax, DWORD dwValue)
{
     PUCHAR pResult = NULL;
     UINT nIter = 0;
     for (nIter=0; nIter<cchMax; nIter++)
     {
         if ( *( DWORD *)(lpBuffer + nIter) == dwValue ) {
             pResult = lpBuffer + nIter;
             break ;
         }
     }
     return pResult;
}
 
//==============================================================================
 
#define Sleep_addr          0xBBBBBBBB
#define DeleteFileA_addr    0xDDDDDDDD
#define ExitThread_addr     0xFFFFFFFF
#define VirtualFree_addr    0xEEEEEEEE
#define _DelProc_addr       0xCCCCCCCC
 
static __declspec ( naked ) DWORD WINAPI _DelProc( LPVOID lpParam)
{
     __asm {
         ; // __this_addr:
         push    ebp                     ;
         mov     ebp, esp                ;
 
         push    0x32                    ; // dwMilliseconds
         mov     eax, Sleep_addr         ;
         call    eax                     ; // ds:__imp__Sleep@4 ; Sleep(x)
 
         mov     eax, [ebp+8]            ;
         push    eax                     ; // lpFileName
         mov     eax, DeleteFileA_addr   ;
         call    eax                     ; // ds:__imp__DeleteFileA@4 ; DeleteFileA(x)
 
         push    0                       ; // dwExitCode, ExitThread 函数的参数
 
         push    8000h                   ; // dwFreeType
         push    0                       ; // dwSize
         mov     eax, _DelProc_addr      ; // __this_addr
         push    eax                     ; // lpAddress
 
         mov     eax, ExitThread_addr    ; // ds:ExitThread
         push    eax                     ; // 将 ExitThread 函数的地址压栈, 这样, VirtualFree 函数返回后就会马上执行 ExitThread 函数
 
         mov     eax, VirtualFree_addr   ;
         jmp     eax                     ; // ds:__imp__VirtualFree@12 ; VirtualFree(x,x,x)
 
         mov     esp, ebp                ; // 以下 3 条指令不会被执行的.
         pop     ebp                     ;
 
         ret     4                       ;
     }
}
 
static __declspec ( naked ) DWORD WINAPI _DelProc_end( LPVOID lpParam)
{
     __asm {
         ret     4                       ;
     }
}
 
BOOL BuildRemoteThreadCode(OUT PUCHAR lpCode, UINT cchMax, DWORD dwRemoteBegin)
{
     UINT nCodeLen = 0;
     PUCHAR pIter = NULL;
     DWORD dwFnAddr = 0;
 
     if (NULL==lpCode || 0==cchMax) {
         return FALSE;
     }
 
     nCodeLen = ( PUCHAR ) &_DelProc_end - ( PUCHAR ) &_DelProc;
     if (nCodeLen > cchMax) {
         return FALSE;
     }
 
     memcpy (( void *)lpCode, ( void *) &_DelProc, nCodeLen);
 
     {
         pIter = FindDWordFromBuffer(lpCode, nCodeLen, Sleep_addr);
         if (NULL == pIter) {
             return FALSE;
         }
 
         dwFnAddr = ( DWORD ) GetProcAddress(GetModuleHandle(_T( "kernel32.dll" )), "Sleep" );
 
         if (0 == dwFnAddr) {
             return FALSE;
         }
         *( DWORD *)pIter = dwFnAddr;
     }
 
     {
         pIter = FindDWordFromBuffer(lpCode, nCodeLen, DeleteFileA_addr);
         if (NULL == pIter) {
             return FALSE;
         }
 
         dwFnAddr = ( DWORD ) GetProcAddress(GetModuleHandle(_T( "kernel32.dll" )), "DeleteFileA" );
         if (0 == dwFnAddr) {
             return FALSE;
         }
         *( DWORD *)pIter = dwFnAddr;
     }
 
     {
         pIter = FindDWordFromBuffer(lpCode, nCodeLen, ExitThread_addr);
         if (NULL == pIter) {
             return FALSE;
         }
 
         dwFnAddr = ( DWORD ) GetProcAddress(GetModuleHandle(_T( "kernel32.dll" )), "ExitThread" );
         if (0 == dwFnAddr) {
             return FALSE;
         }
         *( DWORD *)pIter = dwFnAddr;
     }
 
     {
         pIter = FindDWordFromBuffer(lpCode, nCodeLen, VirtualFree_addr);
         if (NULL == pIter) {
             return FALSE;
         }
 
         dwFnAddr = ( DWORD ) GetProcAddress(GetModuleHandle(_T( "kernel32.dll" )), "VirtualFree" );
         if (0 == dwFnAddr) {
             return FALSE;
         }
         *( DWORD *)pIter = dwFnAddr;
     }
 
     {
         pIter = FindDWordFromBuffer(lpCode, nCodeLen, _DelProc_addr);
         if (NULL == pIter) {
             return FALSE;
         }
 
         dwFnAddr = ( DWORD ) dwRemoteBegin;
         if (0 == dwFnAddr) {
             return FALSE;
         }
         *( DWORD *)pIter = dwFnAddr;
     }
 
     return TRUE;
}
 
BOOL RemoteDel( DWORD dwProcessID, LPCSTR lpszFileName, DWORD dwTime)
{
     CHAR szFileName[MAX_PATH] = { 0 };
     HANDLE hProcess = NULL;
     DWORD dwCodeLen = 0;
     DWORD dwSize = 0;
     LPVOID lpRemoteBuf = NULL;
     PUCHAR pBuff = NULL;
     DWORD dwWritten = 0;
     DWORD dwID = 0;
     HANDLE hThread = NULL;
 
     // 打开目标进程
     hProcess = OpenProcess(
                    PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE,
                    dwProcessID);
     if (NULL == hProcess)
         return FALSE;
 
     GetModuleFileNameA(NULL, szFileName, MAX_PATH);
     dwCodeLen = ( DWORD )&_DelProc_end - ( DWORD )&_DelProc;
 
     dwSize = dwCodeLen + sizeof (szFileName);
 
     lpRemoteBuf = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
     if (NULL == lpRemoteBuf)
     {
         CloseHandle(hProcess);
         return FALSE;
     }
 
     pBuff = VirtualAlloc(NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
 
     memcpy (pBuff, &_DelProc, dwSize);
 
     BuildRemoteThreadCode(pBuff, dwCodeLen, ( DWORD )lpRemoteBuf);
     memcpy (pBuff+dwCodeLen, szFileName, strlen (szFileName) + 1);
 
     WriteProcessMemory(hProcess, lpRemoteBuf, ( LPVOID )pBuff, dwSize, &dwWritten);
 
     hThread = CreateRemoteThread(hProcess, NULL, 0,
                                  (LPTHREAD_START_ROUTINE)lpRemoteBuf,
                                  ( LPVOID )(( DWORD )lpRemoteBuf + dwCodeLen), 0, &dwID);
 
     CloseHandle(hThread);
     CloseHandle(hProcess);
 
     VirtualFree(pBuff, 0, MEM_RELEASE);
 
     return TRUE;
}
 
int WINAPI _tWinMain( HINSTANCE hInstance, HINSTANCE hPrev, LPTSTR lpCmdLine, int nShowCmd)
{
     CHAR szMe[MAX_PATH] = { 0 };
     DWORD dwId = 0;
 
     EnablePrivilege();
 
     GetModuleFileNameA(NULL, szMe, MAX_PATH);
 
     dwId = FindTarget(_T( "explorer.exe" ));
     RemoteDel(dwId, szMe, 50);
 
     return 0;
}

 

可在参考几篇文章:

http://www.lilu.name/Html/diannaojishu/2009-08/542779136193.html

http://www.builder.com.cn/2008/0323/779913.shtml

http://www.vckbase.com/document/viewdoc/?id=1043

http://tieba.baidu.com/f?kz=14052793

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值