首页
社区
课程
招聘
[讨论]程序运行时自删除代码
发表于: 2008-4-18 12:57 10759

[讨论]程序运行时自删除代码

2008-4-18 12:57
10759
最近在写一个自动升级的程序,要求可以自己给自己升级,那就要首先必须把自己删除掉.于是在网上找了很多的代码,其中有: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上调试通过,如果你有更好的方法,欢迎一起讨论...

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (20)
雪    币: 321
活跃值: (271)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
2
写得不错,顶
2008-4-18 14:39
0
雪    币: 1708
活跃值: (586)
能力值: ( LV15,RANK:670 )
在线值:
发帖
回帖
粉丝
3
我的小收藏,希望对大家有帮助.
上传的附件:
2008-4-18 14:59
0
雪    币: 88
活跃值: (156)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
4
确实第一种方法我这也实现不了,不知道谁成功过
2008-4-18 17:25
0
雪    币: 22
活跃值: (443)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
这样删除的话 杀毒软件估计会提示。
2008-4-18 22:33
0
雪    币: 8729
活跃值: (5195)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
杀毒软件确实要提示。。。
2008-4-18 22:51
0
雪    币: 8729
活跃值: (5195)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7
你的代码我也看过了,都是要等到进程退出之后才能删除,我是在想有没有一种办法,在程序运行的时候就可以删除掉。
2008-4-18 23:02
0
雪    币: 1205
活跃值: (5094)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
8
有一种方法,就是 Windows 的文件保护
2008-4-20 00:39
0
雪    币: 8729
活跃值: (5195)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
9
楼上的能不能说的明白点。。。恕在下无知。。。
2008-4-20 01:22
0
雪    币: 291
活跃值: (213)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
10
API的地址可以调GetProcAddress函数来获取,同版本的系统中各个进程地址空间中kernel32.dll的加载地址是不变的。

简单的方法实用性已经很好了,Simple is better than complex.
2008-4-20 04:25
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
X.exe 用于检测升级,有升级时下载升级包后作相应处理
如果没有 升级就启动主程序Y
2008-4-21 00:06
0
雪    币: 227
活跃值: (91)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
其实win下可以先重命名原来的exe,然后下载新版本为原来的主程序名,然后再退出程序的时候自删除已经改名的exe,第二次启动的时候就已经是新的exe了。
2008-4-21 00:53
0
雪    币: 291
活跃值: (213)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
13
要有一定的权限才行,不过在这人人都是管理员的时代也就无所谓了
2008-4-21 10:15
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
嘿嘿。 看看我的删除自身的代码。
比较傻的做法
#include<windows.h>
#include<stdio.h>
#include "tchar.h"
char pName [MAX_PATH];//本程序路径
HANDLE        hCFN;
        char ch1[200];
        char ch2[MAX_PATH]="1.bat";
        char delsa[]="del \x25\x30\x0d\x0a";
        DWORD filesize1 , wsize;
int main()
{
        GetModuleFileName(NULL, pName, sizeof( pName ) / sizeof(char));
ZeroMemory(&ch1,sizeof(ch1));
        wsprintf(ch1,"del %s \x0d\x0a%s",pName,delsa);
        filesize1=sizeof(ch1);
hCFN=CreateFile(ch2,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,NULL,NULL);
        WriteFile(hCFN,ch1,filesize1-1,&wsize,NULL);
        CloseHandle(hCFN);
        ShellExecute(0, "open", "1.BAT",NULL, NULL, SW_SHOW);
        return 0;
}
2008-4-21 10:23
0
雪    币: 210
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
已经疑惑很多年:
      exe 文件的 IMAGE(硬编码为4)
什么是硬编码,  从什么地方可以查阅到 <硬编码>相关的资料?

何方高人,能指点迷津, 感激之话以无法表述.
2008-4-21 10:40
0
雪    币: 8729
活跃值: (5195)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
16
硬编码 这个东西不好说清楚.我只能给你简单举个例子.
比如我们现在要调用一个函数Beep,我们写程序的时候可以成Call Beep,但是如果我们知道Beep的地址是0x1234,那么我们可以直接Call 0x1234.这样的话,对操作系统的依赖性很强.CALL 0x1234就相当于硬编码.
2008-4-21 14:34
0
雪    币: 101
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
WinExec(PChar('cmd /c erase /F '+GetCommandLine),0);
ExitProcess(0);
自删而已,没必要弄得那么复杂,还是用这种方法吧,对系统有通用性!
2008-4-23 11:19
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
很好用,非常感谢
2008-6-12 08:27
0
雪    币: 160
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
win 2000下成功 xp以上不能成功
2008-6-12 09:19
0
雪    币: 160
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
WinExec(PChar('cmd /c erase /F '+GetCommandLine),0);這句c代碼如何寫呢?
2008-6-12 09:49
0
雪    币: 1602
活跃值: (14)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
收藏?加阅读
2008-6-23 18:17
0
游客
登录 | 注册 方可回帖
返回
//