最近在写一个自动升级的程序,要求可以自己给自己升级,那就要首先必须把自己删除掉.于是在网上找了很多的代码,其中有:Gary Nebbett 钻了系统的一个漏洞,他的程序是关闭了 exe 文件的 IMAGE(硬编码为4),然后用 UnmapViewOfFile 解除了 exe 文件在内存中的映象,接着通过堆栈传递当前程序的路径名缓冲区指针给 DeleteFile() ,实现了程序的自删除。还有通过BAT文件来删除的.
Gary Nebbett的代码如下:
#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;
}
以上代码我这里测试已经不起作用,程序运行时并不能把自己删除.
批处理我就不说了,就是在程序运行完调用一个批处理来不停的循环只到把自己删除.
还有就是两个进程互想升级,这个我觉得没有什么技术含量,也不列入讨论范围.
我今天要说下我的思路和实现,就是用运程线程和内存补丁技术,把删除代码放入其它进程里,等进程结束,然后删除程序.下面结合代码看下
//自定义结构体,用于远程线程的执行参数
typedef struct _RemoteProcessInfo
{
HANDLE hProcess;
TCHAR szProcessImageName[MAX_PATH];
TCHAR szSourceFile[MAX_PATH];
}RemoteProcessInfo,*PRemoteProcessInfo;
hProcess本进程的进程句柄
szProcessImageName本进程的完整路径
szSourceFile这个参数在这里暂是没有用处.
//删除程序的代码,pParam是PRemoteProcessInfo类型
__declspec(naked) void DeleteUpdate(LPVOID pParam)
{
__asm
{
pushad
push ebp
mov ebp,esp
push 0x12C
push 0x2EE
mov eax,_Beep;
call eax
push 0
mov esi,[ebp+0x28] <--------这里是取pParam参数的地址,0x28是参数在栈里的偏移(基于ebp)
push esi <--------pParam.hProcess入栈
mov eax,_WaitForSingleObject
call eax
add esi,4 <--------取pParam.szProcessImageName地址
push esi
mov eax,_DeleteFileW
call eax
mov edi,esi
add esi,0x104 <--------取pParam.szSourceFile地址,这里我们先不管它
push edi
push esi
mov eax,_MoveFileW
call eax
pop ebp
popad
ret
}
}
//上面汇编用的到几个函数的地址
#define WIN_XP_2003
//windows 2003,XP
#ifdef WIN_XP_2003
DWORD _Beep = 0x7C832EC0;
DWORD _WaitForSingleObject = 0x7C821C7B;
DWORD _DeleteFileW = 0x7C80EA51;
DWORD _MoveFileW = 0x7C80E261;
#endif
#ifdef WIN_2000
DWORD _Beep = 0x77E6D511;
DWORD _WaitForSingleObject = 0x77E6B400;
DWORD _DeleteFileW = 0x77E77643;
DWORD _MoveFileW = 0x77E77BB2;
#endif
#ifdef WIN_VISTA
DWORD _Beep = 0x77E00000 + 0x0008655F;
DWORD _WaitForSingleObject = 0x77E00000 + 0x00047730;
DWORD _DeleteFileW = 0x77E00000 + 0x0001AD23;
DWORD _MoveFileW = 0x77E00000 + 0x0005419B;
#endif
其中_Beep用来调试代码用的,上面的汇编代码二进制值为:
byte InjectDataXP_2K3[] = {0x60,0x55,0x8B,0xEC,0x68,0x2C,0x01,0x00,
0x00,0x68,0xEE,0x02,0x00,0x00,0xB8,0xC0,
0x2E,0x83,0x7C,0xFF,0xD0,0x6A,0x00,0x8B,
0x75,0x28,0x56,0xB8,0x7B,0x1C,0x82,0x7C,
0xFF,0xD0,0x83,0xC6,0x04,0x56,0xB8,0x51,
0xEA,0x80,0x7C,0xFF,0xD0,0x8B,0xFE,0x81,
0xC6,0x04,0x01,0x00,0x00,0x57,0x56,0xB8,
0x61,0xE2,0x80,0x7C,0xFF,0xD0,0x5D,0x61,
0xC3};
接下来要做的就是把这些代码注入到其它的进程里,如"explorer.exe"里
代码如下:
void InjectCode(TCHAR* szDeleteImageName,TCHAR* szTempImageName,UINT RmCodeLen)
{
LPVOID pRmCodeMemory;
HANDLE hselfkillProcess;
BOOL bFound = FALSE;
DWORD aProcesses[1024], cbNeeded, cProcesses;
if (!EnumProcesses(aProcesses,sizeof(aProcesses),&cbNeeded))
return;
cProcesses = cbNeeded/sizeof(DWORD);
for (int i=0; i<cProcesses; i++)
{
TCHAR szProcessName[MAX_PATH] = {0};
DWORD PID = aProcesses[i];
hselfkillProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,PID);
if (hselfkillProcess)
{
HMODULE hMod;
DWORD cbNeeded;
if (EnumProcessModules(hselfkillProcess,&hMod,sizeof(hMod),&cbNeeded))
{
GetModuleBaseName(hselfkillProcess,hMod,szProcessName,sizeof(szProcessName));
CString tmpProcessName = szProcessName;
tmpProcessName.MakeLower();
if (tmpProcessName.Compare(_T("explorer.exe")) == 0)
{
bFound = TRUE;
break;
}
}
CloseHandle(hselfkillProcess);
}
}
if (!bFound)
{
return;
}
RemoteProcessInfo rpi;
rpi.hProcess = hselfkillProcess;
lstrcpy(rpi.szProcessImageName,szDeleteImageName);
lstrcpy(rpi.szSourceFile,szTempImageName);
int RemoteOffset = sizeof(InjectDataXP_2K3);
PVOID pRemoteData = InjectDataXP_2K3;
pRmCodeMemory = VirtualAllocEx(hselfkillProcess,NULL,RmCodeLen,MEM_COMMIT,PAGE_READWRITE);
if (pRmCodeMemory)
{
if (WriteProcessMemory(hselfkillProcess,pRmCodeMemory,pRemoteData,RemoteOffset,NULL)) //写入执行代码DeleteUpdate函数
{
if (WriteProcessMemory(hselfkillProcess,(LPVOID)((int)pRmCodeMemory+RemoteOffset),(LPCVOID)&rpi,sizeof(RemoteProcessInfo),NULL))//写入DeleteUpdate函数的参数
{
HANDLE hRemoteThread = CreateRemoteThread(hselfkillProcess,NULL,NULL,(LPTHREAD_START_ROUTINE)pRmCodeMemory,(LPVOID)((int)pRmCodeMemory+RemoteOffset),NULL,NULL);
CloseHandle(hRemoteThread);
}
}
}
}
其中RmCodeLen要大于等 sizeof(InjectDataXP_2K3) + sizeof(RemoteProcessInfo );
然后主程序里调用
int _tmain(int argc, _TCHAR* argv[])
{
TCHAR szFile[MAX_PATH];
if (GetModuleFileName(0,szFile,MAX_PATH) != 0)
{
TCHAR szTmpFile[MAX_PATH];
GetTempPath(MAX_PATH,szTmpFile);
CString tmpstr = szFile;
tmpstr = tmpstr.Right(tmpstr.GetLength() - tmpstr.ReverseFind('\\') - 1);
tmpstr.MakeLower();
tmpstr += _T(".upg");
tmpstr = szTmpFile + tmpstr;
InjectCode(szFile,tmpstr.GetBuffer(),sizeof(InjectDataXP_2K3) + sizeof(RemoteProcessInfo ));
}
}
运行这段代码,听到"滴"的一声,程序退出然后自己删除.
这段代码的不足之处是,程序还是靠外部的方法来删除自身,不能在程序运行的时候把自己删除.还有就是那几个API的地址都是硬编码,程序的兼容性不好.
这段代码在Win2003 SP1 R2上调试通过,如果你有更好的方法,欢迎一起讨论...
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!