自删除技术 (网上收集加理解)
来源:互联网 发布:公司网络屏蔽淘宝 编辑:程序博客网 时间:2024/05/22 12:18
程序的自删除已经不是什么新鲜的话题了,它广泛运用于木马、病毒中。试想想,当你的程序还在运行中(通常是完成了驻留、感染模块),它就自动地把自己从磁盘中删掉,这样一来,就做到了神不知鬼不觉,呵呵,是不是很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 就不会跑飞了(也就是说, 换成别的函数会造成目标进程崩溃的, 特此说明.).
线程函数的最后的平衡堆栈的指令和返回指令是没有意义的, 放这里是为了让反汇编器不会少见多怪.
接下来废话少说, 贴代码:
#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
- 自删除技术 (网上收集加理解)
- Java(29):基础:JVM的理解和使用(收集自网上资源)
- JS事件大全(收集自网上)
- Java中的static 本文收集自网上
- svm的一些理解(网上收集)
- svm的一些理解(网上收集)
- svm的一些理解(网上收集)
- svm的一些理解(网上收集)
- orb算法的理解网上资源收集
- 网上收集的“知乎网”技术方案架构
- Web 建站技术网上资料收集
- 自删除程序技术
- 自删除技术
- 前置自加与后置自加的理解
- Windows下程序自删除资料收集
- svm的一些理解(网上收集整理)
- 无限自动轮播加小点加从网上获取图片
- 自删除技术浅析(一)
- Linux下:分区及格式化命令
- 嘿嘿,Real6410/TE6410/OK6410 支持jlink V8+RVDS2.2 仿真调试了
- 获取中文字符串的首字母
- 解析网卡配置静态Ubuntu IP地址
- ASP.NET网站权限设计实现(一)——使用PowerDesigner进行数据库设计
- 自删除技术 (网上收集加理解)
- OSPF协议介绍
- hadoop学习(五)
- C核心技术手册(二十四)
- C语言的谜题
- 不开心
- 深度探索Activity(1): 系统服务初始化
- JSConf 2010 (二):JS的模式编程、微格式 和 MooTools
- wo