高手挑战:谁能解释这个删除自己的EXE?

plato 2001-06-21 10:39:00
//Windows 2000

#include <stdio.h>
#include <windows.h>

LPVOID pFuncAfterDelSelf;

DWORD WINAPI ThreadFunc(LPVOID lpVoid)
{
DWORD CodeCopy[4096];
HANDLE hThread = GetCurrentThread();
pFuncAfterDelSelf = CodeCopy;
__asm
{
push 0
push 0
push hThread
push ExitThread
push SuspendThread
ret
}
return 0;
}

void __stdcall func(LPVOID func1, LPVOID func2)
{
int i;
for(i=0;i<20;i++)
{
__asm
{
push 1
call func1
push 1000
call func2
}
}
}
void foo(void)
{
}

void main(void)
{
HANDLE hModule = GetModuleHandle(NULL);
HANDLE hThread;
char szModuleName[MAX_PATH];
GetModuleFileName((HINSTANCE)hModule, szModuleName, MAX_PATH);

if(!(hThread = CreateThread(NULL, 0, ThreadFunc, NULL, 0, NULL)))
{
printf("cannot create thread\n");
ExitProcess(0);
}
Sleep(100);
CopyMemory(pFuncAfterDelSelf, func, (DWORD)foo-(DWORD)func);
CloseHandle( (HANDLE)4);
__asm
{
lea eax, szModuleName
push 0
push 0
push hThread
push ExitProcess
push Sleep
push MessageBeep
push ResumeThread
push eax
push pFuncAfterDelSelf
push hModule
push DeleteFile
push UnmapViewOfFile
ret
}
}
...全文
166 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
y_pro 2001-06-21
  • 打赏
  • 举报
回复
这是对系统核心操作有深刻了解的人才能写得出来的,象我这样的
就只有分析一下了,看来要再去研究一下核心?那可是一项苦差事!
mikeluo 2001-06-21
  • 打赏
  • 举报
回复
确实经典
y_pro 2001-06-21
  • 打赏
  • 举报
回复
呵呵,想不到在我写解释的时候上面已经有那么大一堆东西了!
y_pro 2001-06-21
  • 打赏
  • 举报
回复
我的理解:
主程序中的汇编代码是把自己的操作放在进程结束时系统的返回栈
中,因此实际的删除操作是在进程结束时执行的。
从出栈的顺序来看:
首先是在进程退出现场时先UnmapViewOfFile,这是为了减少文件的
FileViewMap记数,因为是删除自己,在程序启动时记数加1,在调用
后记数减1,此时删除文件不会报错,因为实际上操作系统会认为该文件
没有被引用。
其后就是文件删除操作
在文件删除后直到POP 0,是模拟进程退出时的系统动作,而两个POP 0
则是表示进程退出操作结束。
这个代码实际上就是用自己的进程退出操作替代了系统的退出操作,在其中
加上了删除文件的操作。
plato 2001-06-21
  • 打赏
  • 举报
回复
分都给Kevin了,呵呵
Smile_Tiger 2001-06-21
  • 打赏
  • 举报
回复
建议收藏在精品里或者文档里
Kevin_qing 2001-06-21
  • 打赏
  • 举报
回复
程序写得满精致的,不过不知道为什么一定要写的这么复杂?
应该可以使用内存分配函数(virtualalloc)来分配函数指令空间,那就不需要搞这么麻烦来使用栈和线程了。
Kevin_qing 2001-06-21
  • 打赏
  • 举报
回复
同样分析栈:

0
0
hThread
ExitProcess
Sleep
MessageBeep
ResumeThread
szModuleName
pFuncAfterDelSelf
hModule
push DeleteFile
push UnmapViewOfFile

1.ret后返回到UnmapViewOfFile
Ip=UmMapViewOfFile
参数是hmodule
ret=DeleteFile
2.返回到deleteFile,参数szModuleName
ret=pfundelafterself
3.继续返回到pfundelafterself
参数sleep,messagebeep
而pfundelafterself其实就是func,所以这个函数就是func(MessageBeep,Sleep);
4.返回到resumethread
线程退出。
5返回到exitProcess进程退出


aceplus 2001-06-21
  • 打赏
  • 举报
回复
lihai!!!
Kevin_qing 2001-06-21
  • 打赏
  • 举报
回复
上面是对删除文件部分的分析。
下面再看看这个程序扩充的部分。

if(!(hThread = CreateThread(NULL, 0, ThreadFunc, NULL, 0, NULL)))
{
printf("cannot create thread\n");
ExitProcess(0);
}

先建立了一个线程,同样是不能访问磁盘文件的函数。
Sleep(100);
CopyMemory(pFuncAfterDelSelf, func, (DWORD)foo-(DWORD)func);
把func的代码拷贝到线程的局部变量里面(栈)。

然后看主线程返回的部分
seesi 2001-06-21
  • 打赏
  • 举报
回复
妙!
Kevin_qing 2001-06-21
  • 打赏
  • 举报
回复
先看看陆麟的分析。

精妙代码
作者:陆麟
转载请征得作者同意.
2001.1.12



--------------------------------------------------------------------------------

下面的代码由Gary Nebbett写就.Gary Nebbett乃是WINDOWS NT/2000 NATIVE API REFERENCE的作者.乃NT系统一等一的高手.下面就分析一些他的这段代码.
这段代码在PROCESS没有结束前就将启动PROCESS的EXE文件删除了.
int main(int argc, char *argv[])
{
HMODULE module = GetModuleHandle(0);
CHAR buf[MAX_PATH];
GetModuleFileName(module, buf, sizeof buf);
CloseHandle(HANDLE(4));
__asm {
lea eax, buf
push 0
push 0
push eax
push ExitProcess
push module
push DeleteFile
push UnmapViewOfFile
ret
}
return 0;
}
现在,我们先看一下堆栈中的东西

偏移 内容
24 0
20 0
16 offset buf
12 address of ExitProcess
8 module
4 address of DeleteFile
0 address of UnmapViewOfFile

调用RET返回到了UnmapViewOfFile,也就是栈里的偏移0所指的地方.当进入UnmapViewOfFile的流程时,栈里见到的是返回地址DeleteFile和HMODUL module.也就是说调用完毕后返回到了DeleteFile的入口地址.当返回到DeleteFile时,看到了ExitProcess的地址,也就是返回地址.和参数EAX,而EAX则是buffer.buffer存的是EXE的文件名.由GetModuleFileName(module, buf, sizeof buf)返回得到.执行了DeleteFile后,就返回到了ExitProcess的函数入口.并且参数为0而返回地址也是0.0是个非法地址.如果返回到地址0则会出错.而调用ExitProcess则应该不会返回.
这段代码的精妙之处在于:
1.如果有文件的HANDLE打开,文件删除就会失败,所以,CloseHandle(HANDLE(4));是十分巧妙的一手.HANDLE4是OS的硬编码,对应于EXE的IMAGE.在缺省情况下,OS假定没有任何调用会关闭IMAGE SECTION的HANDLE,而现在,该HANDLE被关闭了.删除文件就解除了文件对应的一个句柄.
2.由于UnmapViewOfFile解除了另外一个对应IMAGE的HANDLE,而且解除了IMAGE在内存的映射.所以,后面的任何代码都不可以引用IMAGE映射地址内的任何代码.否则就OS会报错.而现在的代码在UnmapViewOfFile后则刚好没有引用到任何IMAGE内的代码.
3.在ExitProcess之前,EXE文件就被删除了.也就是说,进程尚在,而主线程所在的EXE文件已经没了.(WINNT/9X都保护这些被映射到内存的WIN32 IMAGE不被删除.)

Gary Nebbett果然是WIN系列平台的顶尖高手之一.能写出如此代码.独辟蹊径啊:)

codeJie 2001-06-21
  • 打赏
  • 举报
回复
一样不懂,ai....
plato 2001-06-21
  • 打赏
  • 举报
回复
看不懂啊,那位高人解释解释

16,472

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧