Reverse engineering Windows 95 itself
(Understanding our trade)
by +Rcg
(17 August 1997, slightly edited by fravia)
Courtesy of fravia's page 
of reverse engineering
Well, this is an important addition 
to our +HCU didactic material... This you should read, and head, and 
pray with me that +RCG sends us more!
FIRST PART
1. Introduction
2. First Step
3. Memory Addressing
4. Control Transfers
5. Interruptions/Exceptions
6. Hardware Multitasking
SECOND PART
7. V86 Mode 
8. In/Out ports
9. Debugging
THIRD PART
10. Reversing Engineer of W95 itself
 
        			INTRODUCTION
	I have been thinking for a long time how to write
this document, because I would want it very easy to
follow, and of course, so that you may take profit of it.
	This doc deals with i386 and above architecture, and
is the first step to understand how all the OS based on this
kind of processors work (and specially W95).
	There are a lot of places where you can find info about
this, but my personal experience is that this information is
not very useful, because the writer has most of the time another 
point of view, quite different from our. For example we don't want 
to enter, nor exit, from PM because we don't want to write a 
program (quite the countrary, we want to "reconstruct" programs, 
and Windoze will do it for us if needs be (this doesn't mean we 
do not want to learn it, it means that our accents are different.
	We will use WinIce to learn, just like I did on my
Amiga with Action Replay: I learned assembler on my Amiga
modifying my favourite games, working with running programs
and modify them. Try everything you imagine, and when you
have all understood make your own program using all the new 
things you have learnt.
	My initial idea was to write about VxD, but due to
the scarce information (and the absolute MS dependence) I
think the best solution is to analize how W95 works and create
your own utilities working with full privileges. 
	To make this, we must understand first of all how our
processor works and then take profit of it (for cracking or
for protecting purposes, it amounts to the same :-)
				FIRST STEP	
	And now a little theory, don't worry, only a little.
	These 'new' processors support via hardware  
multitasking, that is a 'grossomodo' way to run more than one
program at the same time with a single processor... different 
tasks must run sequentially each for a short time, so to our eyes, 
these tasks will seem to run simultaneously.
	Other new features are the new memory management, 
the hardware debugging, and some other features that we will 
analyze later. 
	One of the problems associated to this is that task one 
can modify (maliciously or unvolontarly) the code or the data of 
task two.
	To solve this problem, the CPU has some protection mechanisms.
	Basically the protections mechanims deals with the 
privilege levels. When you enter at Protected Mode, you
must create a table (GDT), where you will assign to every 
selector (part of memory) differents characteristics.
	A selector is for example this: CS=28 (like W95)
then in the GDT (Global Descriptor Table) you will say for
example that the Selector 28 is the memory between 00000000
and 00003000, where we will put Code working at privilege 
level 2 (with some other characteristics). Think of that
like if it were a Real Mode Segment, but with the limit of 4Gb 
(not only 64Kb) and with some attributes, like if it is data
or code, or if it can be modified or not ...
	So, we must define some selectors and their attributes,
minimum for the Data, Code, Stack and some others.
	
	Let's see that in real life:
Fire Winice and then write:
	GDT
	you will see more or less these lines:
GDTbase=C011E37C   Limit=01F7
 Sel     Type         Base         Limit    DPL Attributes
0008    Code16      0000DF40     0000FFFF    0    P   RE
0010    ......      ........     ........    .    .   ..
0018    TSS32       C000AEBC     0000AEBC    0    P   B
0020    ......      ........     ........    .    .   ..
0028    Code32      00000000     FFFFFFFF    0    P   RE
0030    Data32      00000000     FFFFFFFF    0    P   RW
..
..
..
00C0    LDT         80095000     00002FFF    0    P
..
..
..
and more...
	Let's first intruduce some important new registers
and structures.
	Registers:
	There are 4 new ( called control) registers, named
CR0, CR1, CR2 and CR3.
	CR0: Machine Control Register (bits important for us)
	bit #15: Pagind enabled (memory control)
	bit #0: Protected Mode Enabling (1=enabled)
	
	CR1: Not used (at least in the i486)
	
	CR2: Page Fault Linear Address Register
	Holds the 32-bit linear address that caused the page
fault.
	CR3: Page Directory Base Address Register (more later)
	bits #31-12: Page Directory Base Register
	New flags register called EFLAGS (Extended Flags)
	bit #17: Virtual V86 mode (1=V86 mode)
	bit #16: Resume Flag
	bit #14: Nested Task
	bit #12-13: Input/Output Privilege level
	
	
	Debug Registers (DR0-DR7)
	System Address Registers (GDTR, IDTR, LDTR, TR).
	
	Structures:
	GDT structure is an array of 8 byte, there are some
types of Segment Descriptor we can define:
		Data Segment
	bits #0-15: Segment Limit
	bits #15-31: Segment Base 15..0
	bits #32-39: Segment Base 16..23
	bit #40: Accessed
	bit #41: Writable
	bit #42: Expand-Down
	bit #43: 0
	bit #44: 1
	bit #45-46: DPL (Descriptor privilege level)
	bit #47: Present
	bit #48-51: Segment Limit 16..19
	bit #52: Available for programmer use
	bit #53: 0
	bit #54: Big
	bit #55: Granularity
	bit #56-63: Segment Base 24..31
 
		Executable Segment
	bits #0-15: Segment Limit
	bits #15-31: Segment Base 15..0
	bits #32-39: Segment Base 16..23
	bit #40: Accessed
	bit #41: Readable
	bit #42: Conforming
	bit #43: 1
	bit #44: 1
	bit #45-46: DPL (Descriptor privilege level)
	bit #47: Present
	bit #48-51: Segment Limit 16..19
	bit #52: Available for programmer use
	bit #53: 0
	bit #54: Default operation size
	bit #55: Granularity
	bit #56-63: Segment Base 24..31
	
		Task Gate Descriptor
	bits #0-15: Reserved
	bits #15-31: TSS Segment Descriptor
	bits #32-39: Reserved
	bit #40: 1
	bit #41: 0
	bit #42: 1
	bit #43: 0
	bit #44: 0
	bit #45-46: DPL (Descriptor privilege level)
	bit #47-63: Reserved
		Call Gate Descriptor
	bits #0-15: Offset in Segment 15..0
	bits #15-31: Segment Selector
	bits #32-36: Count
	bit #37-41: 0
	bit #42: 1
	bit #43: X (0=286 Call Gate  1=486 Call Gate)
	bit #44: 0
	bit #45-46: DPL (Descriptor privilege level)
	bit #47: Present
	bit #48-63: Offset in Segment 31..16
		Interrupt/Trap Gate Descriptor
	bits #0-15: Offset in Segment 15..0
	bits #15-31: Segment Selector
	bits #32-36: Reserved
	bit #37-38: 0
	bit #39: T (0=286 Int. Gate  1=486 Int. Gate)
	bit #40-42: 1
	bit #43: X (0=286 1=486)
	bit #44: 0
	bit #45-46: DPL (Descriptor privilege level)
	bit #47: Present
	bit #48-63: Offset in Segment 31..16
	
		LDT Descriptor (Local Descrip. Table)
	
	bits #0-15: Segment Limit
	bits #15-31: Segment Base 15..0
	bits #32-39: Segment Base 16..23
	bit #40: 0
	bit #41: 1
	bit #42: 0
	bit #43: 0
	bit #44: 0
	bit #45-46: DPL (Descriptor privilege level)
	bit #47: Present
	bit #48-51: Segment Limit 16..19
	bit #52: Available for programmer use
	bit #53: 0
	bit #54: 0
	bit #55: Granularity
	bit #56-63: Segment Base 24..31
	
	
		TSS Descriptor	
	bits #0-15: Segment Limit
	bits #15-31: Segment Base 15..0
	bits #32-39: Segment Base 16..23
	bit #40: 1
	bit #41: Busy
	bit #42: 0
	bit #43: X (0= 286 TSS  1=486 TSS)
	bit #44: 0
	bit #45-46: DPL (Descriptor privilege level)
	bit #47: Present
	bit #48-51: Segment Limit 16..19
	bit #52: Available for programmer use
	bit #53: 0
	bit #54: 0
	bit #55: Granularity
	bit #56-63: Segment Base 24..31
	
	
	Now try at your own to get the same values that 
WinIce gives us. How?
	Write GDT, and if it says GDTbase=C0113E7C, then
E C0113E7C, ah!!! by the way the first entry (selector 0)
must be all zeroes.
	All Ok?.....Let's continue.......
	GDTbase is the memory location of the GDT table, and
can be obtained using:
		lea 	esi,_64bitbuffer
		sgdt	fword [esi]		;Store GDT at [esi]
	This can be done only if your descriptor code segment
is running at privilege level 0.
		Segment Selector Format:
	bit #0-1:RPL (Requested Privilege Level)
	bit #2: Table Indicator
	bit #3-15:Index
	CS=28h ==> 101000b (Kernel)
		Index = 101 => 5 (Entry number 5)
		Table Indicator = 0 (Global Table)
		Privilege Level = 0			
	CS=137h ==> 100110111b (Aplication Programms)
		Index = 26h = 38
		Table Indicator = 1 (Local Table)
		Privilege Level = 3 (Opps!!!)
	
	You can see how W95 assigns to our programs, the
lowest privilege level (3), this is in order that our
programs don't affect seriously the OS, because we
have some restrictions.
	Restrictions the i486 has:
	* Some instructions are disabled, i.e. sgdt., 
and can be executed only at level 0.
	* Data or Code from a higher priv. level
cannot be read nor modified.  
	* Code from a lower level can't jump directly
to a higher level.
	
	* Every Segment Descriptor has a limit, so if
an aplication tries to write outside of the limits,
then the processor generate an exception (which will 
be intercepted by the OS, which in turn will perform 
some operations to solve the problem).
	* The Stack Selector must have exactly the
same Privilege Level than the CS Selector.
	* There is one exception to these rules, you
could see that the Executable Descriptor can be
marked as a Conforming, this means that you can
load this Selector in the DATA segment register
without any restriction. 
	At this time you can be asking yourself how
an aplication (level 3) can execute the OS funtions
(level 0) if you can't jump to them.
	But, before answering this question, let's analyze
first the memory addressing.
				MEMORY ADDRESSING
	There are three concepts you must undertand first of all:
		1. Logical Address
		2. Linear Address
		3. Physical Address
	Physical memory is the 'real' address, the number that the
processor puts in the bus to access a data in memory, so the
interrupt table is always at the beginning of the memory at 0, so 
if the CPU wants access to this memory location, it must put 0 on 
the address BUS to read the value stored in it.	Another example,
the video memory is at A0000, so the CPU puts that value on the
BUS.
	Obviously, the best way to access memory is putting directly
this value into a register and get the value stored in it, this is
the way Motorola processors work, yet Intel's do not work this way.
	x86 processors combine a segment register with an offsset to
obtain the physical address, this is the solution they took in 16
bits processors times... now that the registers are 32bit long
this way is obsolete, but Intel decided to prserve continuity with 
the past manteining compatibility with the old DOS programms, 
so in Protected Mode the segment:offset method is used too, but 
in a different way.
	Logical address is the combination between segment:offset,
for example if you have a GDT like this:
 Sel     Type         Base         Limit    DPL Attributes
0008    Data32      00000000     FFFFFFFF    0    P   RW
0010    Data32      000A0000     0000FFFF    0    P   RW
..
..
	if you want to read the video memory, you can do it in two ways:
		mov ax,08h	
		mov es,ax	;Selector 8
		mov ax,es:[A0000]
	or:
		mov ax,10h
		mov es,ax	;Selector 10h
		mov ax,es:[0]
 
	In both examples, es:[????????] is the logical address,
and after translating it through the Descriptor Table, we get
the Linear address:
	In the first example:
	Linear Address = Base of Selector 8 + offset =>
							 0+A0000 = A0000
	In the second:
	Linear Address = Base of Selector 10h + offset =>
							 A0000+0 = A0000
 
	After that, x86 have two ways to translate a linear 
address into a physical address, checking bit 31 of CR0
register, the CPU knows if it is in Segment or in Paging
mode (W95 works in Paging mode).
	If the mode is Segmentation, the the Phys. Add. is the
Linear Add., but if we are in Paging mode, the CPU will made
another translation.
	PAGING TRANSLATION
	This mode is a way to manage memory though an index, just
supose you have a very big library, and when you want read a
book, you go to your book index and look where it is stored, and
then go to your library and pick up it. Now, you lost one book, 
and buy another different, then simply you store the new book in
the hollow location of the other one and write down in the index 
the new book location, this is a good way to exploit the library 
space without wastes.
	Well, the i486 books (pages) are 4Kb long, so if
theoretically we can address up to 4Gb, we need a 4Gb/4Kb=1Mb of
indexes if each is 4 bytes long, our index will be 4Mb long, that
is too much for us, we can't waste as much as this.
	So, the solution is create a new index only when really 
necessary, so we can have a main index and a secondary index,
so long as the secondary index is not full we don't create a new 
main index (let's better understand it with an example).
	Linear Address in paging mode has the following meaning:
		Bit31-22: Directory Entry (Main index)
		Bit21-12: Page Entry (Secondary Index)
		Bit11-00: Offset
	The Page Entry format is:
		Bit31-12: Page Address
		Bit11-09: Available for the OS
		Bit08-07: Must be 00	
		Bit6: Dirty (Page has been modified)
		Bit5: Accessed
		Bit04-03: Must be 00
		Bit2: User (0) /Supervisor (1)
		Bit1: Read/Write (1) or Read (0) 
		Bit0: Present
	The physical address of the Page Directory is stored
in the CR3 register.
	Suppose that our CR3 register reads 5DE000h.
	So if our linear address is: 
		403499 ====> 10000000011010011001b
		Directory Entry = 1h
		Page Entry = 3h
		Offset = 499h
Phys. Add.	Data stored in memory (remember that are inverted)	 
5DE000   		67,C2,00,C0 (0)
5DE004		67,C2,55,C9 (1)
5DE008   		   .. 
..		   	   ..
..		   	   ..
5DEFFC	   	   ..   (4095)
	Directory Entry 1 is: C955C267 ('uninverted')
	Physical Page Address is: C955C000  (4Kb aligned)
	Attributes are: 267h = 001 00 11 00 111 
				    
	OS flag=001
	Dirty=1
	Accessed=1
	User=1
	Read/Write=1
	Present=1
	W95 define the OS flag as:
		000=VM
		001=System
		010=Reserved
		011=Private
		100=Reserved
		101=Relock
		110=Instance
		111=Hooked
	
Phys. Add.	Data stored in memory (remember that are inverted)	 
C955C000   		67,C2,30,C2 (0)
C955C004		67,C2,25,C4 (1)
C955C008   		   .. 
C955C010	   	67,C2,34,C3 (3)  
..                   ..
..                   ..
..		   	   ..
C955CFFC	   	   ..   (4095)
	Page Entry 3 is: C425C267 ('uninverted')
	Physical Page Address is: C425C000  (4Kb aligned)
	Attributes are: 267h = 001 00 11 00 111 
	And finally if we add the offset to the Phys. Add. we
obtain the desired value:
	Phys. Add. = C425C000+499h=C425C499
	Remember that a page is only 4Kb long.
	And now, think a little and answer these questions?
	Why all the W95 programs are executed with:
		CS= 137h
		EIP= 401000-7FFFFF range ?
	Does this fact mean that all the programs are in the
	same physical memory location (very illogical)?
	Answer:
	137h=100110111b ==>
			Selector Index=26h
			Table Indicator=1 (Local Table)
			DPL=3 (Lowest privilege level)
	Windows uses Paging mode, so the linear address of
137:401000 mean:
		Directory Entry = 1h
		Page Entry = 1h
		Offset = 000h
	When paging is enabled, a task can only have one page
directory table but several second level page tables.
	So to perform a task swap, W95 only needs to modify
the page directory second entry (1h) to point to the new
task physical address, but to our eyes all tasks are running
at the same linear address.	
	Note that a program has a consecutive linear address,
but not necessary consecutive physical address, for example:
		cs:401FFA 	cmp 	eax,1505h
		cs:402002	jne	402957
	the first instruction can be at C734FFA phys. address and the
second can be at C335002 phys. address, it depends on the second 
table (remember the lost book).
	Finally, if the program accesses a page that is marked
as not present, a fault exception is generated, then the OS
will make the necesary actions (like load it from the exchange
file).
	A practical example: 
	I have used Zmud but you can use other programs or
even windows 95 itself.
	bpx GetLocalTime
	fire zmud
	after a few F11 you will be at:
	137:40749E  E8E3DCFFFF		CALL KERNEL32!GetLocalTime
	137:407499  668B4C240E		MOV CX,[ESP+0E]  7*4=1Ch
	OFFSET=499	
	
	Now type:
	CPU
	and you will obtain:
	EAX= .....
	ESI= .....
	DS = .....
	CR0=8000001B  PE MP .....
	CR2=..
	CR3=005DE000
	..
	..
	for us is only important that we are in Paging Mode, bit31
of CR0 register and the CR3 register, the physical address of the
Directory Page Base.
	Now type:
	phys 5DE000 (obtain the linear address of a phys. add.)
	you will obtain: (I think that is always the same linear
				address, but I'm not too sure)
	C197A000	
	FFBFE000 (Ignore this)
	now type:
	e C197A000 
	you can also obtain this using the peek command.
	Type:
	peek d 5DE000 (for the first entry 0h)
	peek d 5DE004 (for the second entry 1h)
	the answer for the second entry is: (in my case)
	0xC1C267 ===> So the Secondary table phys. add. is:
		
	C12000+1Ch=C1201Ch
	typing again:
	peek d C1201C
	we get:
	0xC1F267 ===> The real phys. add. is:
	C1F000+offset (499h) = C1F499h
	typing finally:
	peek d C1F499:
	0x244C8B66
	that corresponds to the opcodes of our actual eip.
	
	Last question, if W95 operates in Paging Mode, How
Sice obtain the value stored in memory if we give it a
phys. address, remember that the CPU transforms the value
to a phys. address, reading CR3 register we obtain a phys.
address, but how we can transform it to a linear add. if
we don't know where is the directory table?
	I thpought about this for a while, then I tried the following:
	Fire Sice when it executes a ring0 code, then put
these instructions and execute them:
		mov	eax,CR0
		mov	esi,CR3
		and   eax,7fffffff	;Segment Mode
		mov	CR0,eax
		mov	ebx,[esi]	;Now it is the phys. add.
					;because we are in seg. mode
		or 	ax,80000000
		mov	CR0,eax		;Paging Mode
	the result was a miserable crash, why?
	
	Because in the inst. next to the mode change EIP
points to a unexpected memory location so the inst.
executed wasn't the mov ebx,[esi].
	I think that Sice has part of it code in the Conventional
memory, so if you change from Paging to Segment Mode, the memory
location is the same, so it don't crash.....do you agree with
me? Do you know the correct answer? Do you have other ideas?
				CONTROL TRANSFERS
	
	There are three kinds of control transfers:
	1. From one privilege level to another in the same task.
	2. From one task to another (not necesary at the
       same privilege level, Windows95 does not use it).
	3. From PM to V86 mode (or viceversa).
	Remember that each privilege level in one task
must have its own stack segment with the same privilege
level.
	TRANSFERS BETWEEN DIFFERENT LEVELS IN THE SAME TASK
	A task has four levels, every one of them is independent
from the others. Each level must define its own Stack and Code
segments. Any transfer that changes the CPL (current privilege
level) must change the the privilege level of the stack at
the same time.
	HIGHER TO LOWER LEVEL
	Every time the OS gives control to an application, this transfer 
does not check the privilege levels. Note that CS and SS must 
be loaded at the same time! To be able to make this, we store 
in the stack the SS,ESP,CS and EIP and then with the RETF and IRET
we give control to the lower level code (remember that these
instructions load the registers at the same time).
	LOWER TO HIGHER LEVEL
	An application needs to execute OS Code, this transfer
is dangerous for the integrity of the OS, so it must be
controlled, How? Using the GATE Descriptors, these descriptors
points directely to a function of the OS (see the GATE
descriptor format) that 'regains' the control.
	The GATE can be a Interrupt Gate, a Task Gate, or
a Call Gate. W95 uses the Interrupt GATE, so any time that
an application wants to return control to the OS (or
execute part of its code) it will execute a GATE Interrupt
(W95 uses Int. 30).
(Extracted from Soft-Ice manual)
-------------------------------------------------------
Understanding Transitions From Ring-3 to Ring-0
Many times when tracing into code, under Windows 95,
you arrive at either an INT 0x30 or an ARPL. Both are
methods for making a transition from Ring-3 to Ring-0.
When you wish to follow the ring transition, you can
save yourself the time and effort of stepping through a
large amount of VMM code by using the G(o) command to
execute up to the address shown in the disassembly.
Windows 95 uses the following methods to transition
Ring-3 code to Ring-0 code:
&127; For V86 code, Windows 95 uses the ARPL instruction,
which causes an invalid opcode fault. The invalid opcode
handler then passes control to the appropriate VxD.
The ARPL instruction is usually in ROM. Windows 95 uses
only one ARPL and it varies the V86 segment:offset to 
indicate different VxD addresses. For example, if the
ARPL is at FFFF:0, Windows 95 uses the addresses FFFF:0,
FFFE:10, FFFD:20, FFFC:30 and so on.
The following example shows sample output for
disassembling an ARPL:
FDD2:220D ARPL DI,BP ; #0028:C0078CC9 IFSMgr(01)+0511
&127; For PM code, Windows 95 uses interrupt 0x30h. Segment
0x3B contains nothing but interrupt 0x30 instructions,
each of which transfers control to a VxD.
The following example shows sample output for
disassembling segment:offset 3B:31A:
003B:031A INT30 ; #0028:C008D4F4 VPICD(01)+0A98
003B:031C INT30 ; #0028:C007F120 IOS(01)+0648
003B:031E INT30 ; #0028:C02C37FC VMOUSE(03))00F0
003B:0320 INT30 ; #0028:C02C37FC VMOUSE(03))00F0
003B:0322 INT30 ; #0028:C023B022 BIOSXLAT(05)=0022
003B:0324 INT30 ; #0028:C230F98 BIOSXLAT(04)=0008
003B:0326 INT30 ; #0028:C023127C BIOSXLAT(04)=02EC
------------------------------------------------------	
	
	W95 gets the Return address after the Int. and
using it with a table gets the VxD entry function.
	
	Exception to this is the Code Segment marked
as CONFORMING, so if you make a JUMP/CALL to this
king of Segment, the CPU reacts giving this Segment 
the same privilege level of the Caller (and executes 
the Code without problems).
			INTERRUPTIONS/EXCEPTIONS
	In PM interrupts and exceptions are used as a
control-transfer methods.
	Exceptions are generated by the CPU during the
execution of an instruction.
	There are maskable/unmaskable  interrupts. 
Unmaskable can be enabled/disabled using the EFLAGS
register (IF=0 means that the int. will not occur).
	Maskable int. are Ints. from 0-20h.
	Unmaskable from 21h-0FFh.
	As the same maner as the GDT, there are an 
Interrupt Descritor Table (IDT) where the CPU will
get the Code it must execute and if can be done (it
depens on the DPL assigned).
	Using SoftIce write IDT and:
Int   Type   Sel:Offset Attributes Symbol/Owner
IDTbase=C000ABBC Limit=02FF
0000 IntG32 0028:C0001200 DPL=0 P VMM(01)+0200
0001 IntG32 0028:C0001210 DPL=3 P VMM(01)+0210
0002 IntG32 0028:C00EEDFC DPL=0 P VTBS(01)+1D04
0003 IntG32 0028:C0001220 DPL=3 P VMM(01)+0220
0004 IntG32 0028:C0001230 DPL=3 P VMM(01)+0230
0005 IntG32 0028:C0001240 DPL=3 P VMM(01)+0240
0006 IntG32 0028:C0001250 DPL=0 P VMM(01)+0250
0007 IntG32 0028:C0001260 DPL=0 P VMM(01)+0260
0008 TaskG  0068:00000000 DPL=0 P
0009 IntG32 0028:C000126C DPL=0 P VMM(01)+026C
000A IntG32 0028:C000128C DPL=0 P VMM(01)+028C
..
..
..
0030 IntG32 0028:C001B04  DPL=3 P Alloc_PM_Call_Back+4E
..
..
	Now see how the Int. 30 can be called by
applications at level 3 (if you modify and put
DPL=0 Windows 95 will crash due to Protecting Problems,
try it!!!!).
	How?
	IDTbase=C000ABBC, so E C000ABBC+(30*8)
	Now change the EE with 8E, again IDT and you
will see Int. 30 DPL=0.
	x
	and ...... W95 crash inmediately.
	
	Because Int./Exc. can occur at any time and the
DPL is unpredictable, it is necessary to avoid this 
situation... there are two possible solutions: putting the 
handler at level 0 or in a CONFORMING Code Segment.
	After entering to the handler, the stack contain
the next registers:
	No Priv. Level Change  		Priv. Level Change
		old eflags				old ss
		old cs				    old esp
		old eip				    old eflags
		error code				old cs
							    old eip
							    error code
	because when there is a level change, the CPU changes 
the stack selector according to the level (this information
is stored in the TSS segment of the current task, (look at 
the next chapter).
	Vector Number	Description		  Error Code
		0		Divide Error		    No
		1		Debug Exception		    No
		2		NMI Interrupt		    No
		3		Instruction Breakpoint  No
		4		INTO-detected overflow	No
		5		BOUND range excedeed	No
		6		Invalid Opcode		    No
		7		Copro. not available	No
		8		Double Fault		    Yes
		9		Copro. Segment Overrun	No
		10		Invalid TSS			    Yes
		11		Segment not present   	No
		12		Stack Fault			    Yes
		13		General Protection	    Yes
		14		Page Fault			    Yes
		15		Reserved			    --
		16		Copro Error			    No
		17-31	Reserved			    --	
		31-255	Maskable Interrupt	    --
		Error Code format:
	bit #0: External (1 means an external event causes the exception).
	bit #1: I (Exception relates to the IDT)
	bit #2: Table Indicator (0=GDT 1=LDT)
	bit #3-15: Selector that caused the exception.
	bit #16-31: Reserved
		Page Fault Error Code format:
	bit #0: (1 means that the page-level protection
		   violation. 0 means page not present)
	bit #1: (1 Write operation fault. 0 Read )
	bit #2: (1 Supervisor fault. 0 User fault)
	bit #3-31: Undefined.
			HARDWARE MULTITASKING	
	Multitasking is suported via Hardware directly
(although W95 does not use it, due to speed problems),
but how?
	Well in our GDT we can define TSS Descriptors,
where the processor will store some important parameters
before the task switch (registers, and some others like
LDT, CR3...).
	The Task State Segment (TSS) format is:
	byte #00-01: Back Link to previous TSS
	byte #02-03: Reserved
	byte #04-07: ESP0
	byte #08-09: SS0
	byte #10-11: Reserved 
	byte #12-15: ESP1
	byte #16-17: SS1
	byte #18-19: Reserved 
	byte #20-23: ESP2
	byte #24-25: SS2
	byte #26-27: Reserved
	byte #28-31: CR3
	byte #32-35: EIP
	byte #36-39: EFLAGS
	byte #40-43: EAX
	byte #44-47: ECX
	byte #48-51: EDX
	byte #52-55: EBX
	byte #56-59: ESP
	byte #50-63: EBP
	byte #64-67: ESI
	byte #68-71: EDI
	byte #72-73: ES
	byte #74-75: Reserved
	byte #76-77: CS
	byte #78-79: Reserved
	byte #80-81: SS
	byte #82-83: Reserved
	byte #84-85: DS
	byte #86-87: Reserved
	byte #88-89: FS
	byte #90-91: Reserved
	byte #92-93: GS
	byte #94-95: Reserved
	byte #96-97: LDT
	byte #98-99: Reserved
	byte #100-101: Reserved
	byte #101-102: I/O Map Base  
	
	So if you want to switch between tasks, you must
first load the Task Register (TR) with the current
task segment selector (TSS) and the jmp or call to
the other task segment descriptor.
	mov	ax,task0_TSS_selector
	ltr	ax
	call/jump 	far task1_TSS_selector
	You can create for every task you have a different
Task Selector.
	Corrections, addings and comments are welcomed.
+Rcg 1997
   
 (c) +Rcg 1997. All rights reserved
You are deep inside fravia's page of reverse engineering,  
choose your way out:
homepage 
 
links  
anonymity  
+ORC 
students' essays 
academy database 
tools 
cocktails 
antismut CGI-scripts 
search_forms 
mail_fravia 
Is reverse engineering illegal?