下面的代码由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系列平台的顶尖高手之一.能写出如此代码.独辟蹊径啊:) 

解决方案 »

  1.   

    zara(Kyrie eleison)
    你的这段话我早就看了,
    我现在弄不明白DeleteFile的参数究竟是module 呢还是buf?
    另外,我的问题还没解决呢?怎么样在程序删除后退出前在调用我的另外一个函数??
      

  2.   

    void  myfun()
    {
    printf("I'm dieing.\n");

    }typedef void PFUN(); 
    void main()
    {
    int i=0;
    PFUN *pmyfun;
    HMODULE module = GetModuleHandle(0);    CHAR buf[MAX_PATH];    GetModuleFileName(module, buf, sizeof buf);    CloseHandle((HANDLE)4);

    pmyfun = myfun;
    //UnmapViewOfFile(module);    __asm {
            
    lea    eax, buf
            push    0
            push    0
            push    eax
            push    ExitProcess
            push    module
            push    DeleteFile
            push    UnmapViewOfFile
            push pmyfun
    ret
        }
    }
    直接把myfun压栈得到的是错误的地址。
      

  3.   

    DeleteFile的当然是buf。
    更精妙的是,把所需要的位于自己的执行映像中的代码全部压进了栈,其他需要的代码都位于系统DLL中,使得解除映像不丢失代码。
      

  4.   

    repus()老兄:
    DeleteFile跟buf也就是eax中间还隔了module和ExitProcess,怎么参数会是buf呢?那ExitProcess 的参数是不是module呢?
    这里是怎么区分函数与参数的啊?能不能讲详细点?
      

  5.   

    那就是人家黑客的本事了。
    那几个函数的用户态存根代码中使用这样的指令传递参数的:
    push [esp+4]
    push [esp+8]

    只有看过代码才知道那个是参数,哪个是返回地址。
      

  6.   

    BTW:
    我今天为此浪费了两个多小时,我要被老板骂了。
    :-)
      

  7.   

    另外,你是在没有删除文件前调用的myfun(),我想要在删除文件后调用myfun()最后才结束程序。
    我将push  pmyfun 放到push  ExitProcess 和 push  module之间,可是运行后出错。
    究竟该怎么放?放哪里?
      

  8.   

    你把push指令调换位置,首先要算准堆栈偏移。
    如果要在删除文件后运行代码,要将该函数的全部代码压入堆栈,而不仅是起始地址。
    要是那个函数中没有调用别的函数还有希望,否则算了吧。
    代码要反复编译计算,我没有精力试了,对不起。
      

  9.   

    我刚才试了一个方法,就是将modul和buf都改为另外一个文件,结果删除成功,可是接下来就会出现一些内存错误,我估计到最后的返回地址错了。
    如果我要在执行这段代码之后能够继续执行这段汇编语句后面的C语句,那么最先push的应该是什么呢?
      

  10.   

    首先,要搞明白那几个函数的参数
    exitprocess的参数是(eax,0,0),因为是从右->左压栈的
    deletefile的参数是module
      

  11.   

    repus()说ExitProcess的参数是buf 而你又说不是。
    这参数究竟是怎么给的啊?
    另外,我的问题没人回答吗?
    就是在删除文件后,怎么样再调用我的另一个函数,执行完那个函数之后才最后退出。
    这样究竟该怎么做啊?
      

  12.   


    非常感谢 repus() 的热情帮助,不过我的问题依然没有解决,那就是:怎么样在删除了执行文件后,调用我的另一个函数,等执行完我那个函数后才退出程序。该怎么去实现呢??另外我想纠正 freebsd_cn(l0v3hx) 的一个个错误:DeleteFile的参数不是modul而是buf的内容即文件名。
      

  13.   


    非常感谢 repus() 的热情帮助,不过我的问题依然没有解决,那就是:怎么样在删除了执行文件后,调用我的另一个函数,等执行完我那个函数后才退出程序。该怎么去实现呢??另外我想纠正 freebsd_cn(l0v3hx) 的一个个错误:DeleteFile的参数不是modul而是buf的内容即文件名。
      

  14.   

    你说deletefile的参数是buf,那么我问你,
    push 0
    push 0
    push eax
    push exitprocess
    push buf
    push deletefile
    这样压参数可以吗???
      

  15.   

    freebsd_cn(l0v3hx)
    真的是这样,
    不信,你可以DEBUG跟踪看看。
    我当象你那样认为的,可是错了。因为它的参数偏移量有时是+4有时是+8
    我也弄不明白为什么这样。
      

  16.   

            lea    eax, buf
            push    0                 ;ExitProcess(0)
            push    eax               ;DeleteFile(buf)
            push    ExitProcess
            push    module            ;UnmapViewOfFile(module)
            push    DeleteFile
            push    UnmapViewOfFile如果你想加进去自己的函数,应该这样        lea    eax, buf
            push    0                 ;ExitProcess(0)
            push    yourparameter     ;yourfunction(yourparamenter)
            push    ExitProcess
            push    eax               ;DeleteFile(buf)
            push    yourfunction      
            push    module            ;UnmapViewOfFile(module)
            push    DeleteFile
            push    UnmapViewOfFile不过,很不幸,由于UnmapViewOfFile的作用,等到yourfunction的时候,进程的内存已经被释放了,地址跳转倒很正常,不过代码已经全没了,你自己可以试试看。另外,还有一点,这些函数都必须是_stdcall类型的,也就是被调用者清理栈中的参数。
      

  17.   

    呵呵,应该采用naked协定,可自由安排压栈顺序,返回时直接插入一条ret指令,根本不用清栈。可是我看他除了想做病毒,没有什么别的理由可以解释,大侠好自为之。
      

  18.   

    to: freebsd_cn(l0v3hx)
        kkk16() 
    ExitProcess 的参数是0
    DeleteFile  的参数是eax也就是buf
    UnmapViewOfFile 的参数是module
    他们都是__stdcall,自己清理堆栈,都用的是
    ret 4
    当UnmapViewOfFile 执行完时(调用ret 4之后)堆栈如下:
    24  0 
    20  0 
    16  offset buf 
    12  address of ExitProcess