The answer to both questions is, look at the segment descriptor in the
GDT/LDT. The DPL field tells you the ring for that particular segment.
Bit 2 of your segment selector is 0 if it’s referencing a segment in the
GDT, 1 if in the LDT. If you and the segment selector with 0xfffffff8, the
result is the offset of the segment descriptor in the GDT or LDT. You can
get the base through an LGDT/LLDT instruction. Bits 13 and 14 of the dword
at offset 4 in the descriptor contain the DPL, that is, ring 0, 1, 2 or 3.
Note that this is totally OS independent, it’s all determined by the
processor architecture.
Keep in mind that the selector also has a ring value (you get it if you and
the selector value with 7), but that’s a *requested* privilege level, as
opposed to the descriptor’s privilege level which is kept in the GDT or LDT.
Also keep in mind that it is possible to play games with segment protection
by fiddling with the “conforming” bit.
Another thing: what’s called “flat” mode is just the arbitrary
self-restriction to using segments that cover the whole of the 4Gb of
addressing space. The segments in flat mode are segments allright. In fact,
it is quite possible to take great advantage of segmentation even while in
flat mode. Flat mode is just a way of using segments where designers
self-restrict to only use 4Gb segments, to make the system kind of look like
pure RISC machines. But segments are still there, available for you to use
if you want to: for example, a great way of passing buffers around is to
encapsulate them in a segment and pass the selector.
Note however the difference between the ring of our pointer and the setting
of the page table protection bits. While the Page Table Entries have a
User/Supervisor bit, that doesn’t tell you which protection ring you’re
under. However, if you have a pointer that lands on a page where the U/S bit
is set to “supervisor”, you’re going to get an exception if your CPL is 3:
the U/S bit protection mechanism defines Rings 0, 1 and 2 as “supervisor”
mode, and Ring 3 as “user” mode. Windows happens to decorate supervisor
pages by setting the U/S bit to 1 in its page tables, but that’s orthogonal
to which ring the pointer’s pointing to: you may have a ring 3 segment
covering the whole of the 4Gb addressing space, and yet half ot the memory
may be hidden from you not because it’s ring 0, but because the OS sets the
U/S bit.
The page U/S bit, by the way, is readily accessible by traversing the page
directory/table tree down, starting at CR3.
Hope this helps !
Alberto.
-----Original Message-----
From: Jan Bottorff [mailto:xxxxx@pmatrix.com]
Sent: Wednesday, November 07, 2001 7:59 PM
To: NT Developers Interest List
Subject: [ntdev] Re: Output Debug Information.
I am working on some code that works in both Ring0 and Ring3. I would like
to know the answers to the following questions:
o Is there an way to determine if I am ring 0 or ring 3?
You could possible look at the protection bits for the current stack
segment. Move the SS register to a general register and look at the bottom
3 bits. An Intel processor manual has the details on decoding them. Note
that the paged virtual memory system also I believe assigns protection on a
per page basis, so the ring number tells you what instructions are valid
although may not tell you about memory protection. Seems like there is also
register that just contains the current processor protection level.
o Is there a way I can tell if a pointer is a ring 0 pointer or a ring 3
pointer?
Are we talking about a flat virtual pointer or a segmented address. Like I
said above, protection happens on a per page basis. Generally, addresses
above a certain value (often 2GB) are kernel addresses and below that are
user addresses. It’s also possible for devices to map devices/memory into
user space (like DirectX surfaces) but don’t offhand know if those look
like kernel addresses with user mode accessibility or if they look like
user mode addresses.
When the processor is running in kernel mode, it has access to both kernel
and user mode addresses, when in user mode, only user mode addresses. You
code, running in either user or kernel mode could be processing a user mode
address, and it makes no difference if your in user or kernel mode.
The thing you have to worry about is what process is currently mapped if
your running in kernel mode. In a DPC or ISR, the process context as
arbitrary, so accessing a user mode address may not be access the process
memory you expect. Kernel addresses are identical no matter what user
process is mapped.
It’s not real common for the same code to be usable in kernel and user
modes. Things like available API’s and available stack space are totally
different. Can I ask where the code is loaded? To be usable in user mode it
must be in a user mode address space, which means that when every other
process is mapped, the code will be inaccessible from kernel mode. Have you
considered having some kernel code, than knows it’s in kernel mode, and
some different code (possible from the same source code) that’s built to
run in user mode.
You are currently subscribed to ntdev as: xxxxx@compuware.com
To unsubscribe send a blank email to leave-ntdev-$subst(‘Recip.MemberIDChar’)@lists.osr.com
You are currently subscribed to ntdev as: $subst(‘Recip.EmailAddr’)
To unsubscribe send a blank email to leave-ntdev-$subst(‘Recip.MemberIDChar’)@lists.osr.com