ionescu007 / SimpleVisor

SimpleVisor is a simple, portable, Intel VT-x hypervisor with two specific goals: using the least amount of assembly code (10 lines), and having the smallest amount of VMX-related code to support dynamic hyperjacking and unhyperjacking (that is, virtualizing the host state from within the host). It works on Windows and UEFI.
http://ionescu007.github.io/SimpleVisor/
1.69k stars 259 forks source link

ShvUtilConvertGdtEntry confusion #44

Closed hypervisor closed 4 years ago

hypervisor commented 4 years ago

I'm quite new to segmentation, so please bare with me.

To get a segment descriptor entry from GDT we have to access the GDT like an array. Each entry in the GDT is 8 bytes, according to the Intel SDM. To my understanding we can therefore access the GDT like:

PSEGMENT_DESCRIPTOR32 Table = ( PSEGMENT_DESCRIPTOR32 )GdtBase;
PSEGMENT_DESCRIPTOR32 Segment = &GdtBase[ Selector.Index ];

Or, what this is equivalent to:

UINTPTR GdtBase = ..;
PSEGMENT_DESCRIPTOR32 Segment;
Segment = ( PSEGMENT_DESCRIPTOR32 )( GdtBase + ( Selector.Index * 8 ) );

Now, while learning, I read the source to a bunch of different hypervisors. Specifically - Gbhv. Gbhv gets segment descriptors from GDT using the exact method above. https://github.com/Gbps/gbhv/blob/master/gbhv/vmx.c#L178

OsSegmentDescriptor = (PSEGMENT_DESCRIPTOR_64)(((UINT64)GdtRegister.BaseAddress) + (SegmentSelector.Index << 3));

The bitshift to the left by 3 is the same as multiplication by 8, so this is basically exactly the same as the code I wrote above. However, you do it completely differently in SimpleVisor, and I'm very confused as to why.

https://github.com/ionescu007/SimpleVisor/blob/master/shvutil.c#L50

gdtEntry = (PKGDTENTRY64)((uintptr_t)GdtBase + (Selector & ~RPL_MASK));

I understand the Selector & ~RPL_MASK part. You're masking away Rpl & Table bits to extract the index from the selector. But you are not multiplying it by the size of a descriptor (8). Since you're casting GdtBase to a uintptr_t - an integer, this is using integer arithmetic (duh), so it's not accessing the entry in the GDT properly. I'm super confused, because I've personally ran SimpleVisor in the past on both a VMware guest, and on my host system - and it worked. Why? Am I missing something, or is this simply a bug?

ionescu007 commented 4 years ago

Hi!

The answer is simple -- those other sources use the selector index (which is kind of odd).

The code in ShvUtilConvertGdtEntry uses the selector itself. The selector is an offset. So you add the offset to the table base.

I hope this helps!