Windows _EX_FAST_REF Pointers and Virtual Machine Introspection

Last week I was working on a VMI-based malware unpacker for Linux and Windows when I came across an interesting problem. I was trying to implement a method that would, given a virtual address and process ID, return the address range of the memory segment it belongs to using VMI.

Implementing this in Linux was no problem for me because it's the OS I'm most familiar with. The implementation boils down to getting the current process' task_struct, looking up the pointer to it's memory mapping (task_struct->mm), and then iterating through its linked list of virtual memory areas (mm->mmap) until a match is found. Pretty straight forward.

Windows seemed a little tricker but very similar. The main difference is while Linux uses a linked list of structures called virtual memory areas, Windows uses structures called virtual address descriptors (VADs) linked into a balanced binary tree. The procedure is fairly similar. Once the current executive process (_EPROCESS) is located in memory, read its VadRoot pointer that, as the name implies, points to the root of the binary tree of VADs and then check the VAD's memory range. Lookup the left child if the range is too high, the right child if the range is too low, and repeat until the desired VAD is located. A straightforward binary search.

So I implemented my VMI function, ran it, and to my surprise it failed. After some debugging, I discovered that when the code read VadRoot, the pointer would always be 3 bytes greater than the actual base virtual address of the root VAD. Here are some examples of addresses that my code read for 64-bit Windows 7, printed in little-endian:

1b 3c 90 02 80 fa ff ff
6b 3d 6a 02 80 fa ff ff
3b 69 8e 01 80 fa ff ff

Why are the 4 least significant bits always 0xb and why was I only having this problem with the VadRoot pointer and no other pointers? Stumped, I asked my question to the libVMI forum and the developer of DRAKVUF kindly pointed out the answer: the Windows kernel sometimes uses a special pointer called a _EX_FAST_REF.

If you take a look at the definition for this type, you will notice something interesting:

typedef struct _EX_FAST_REF
        PVOID Object;
        ULONG RefCnt: 3;
        ULONG Value;

As you can see, the Windows kernel uses the 3 least significant bits as a reference counter. Therefore, in order to read this pointer correctly using VMI, these bits need to be masked out after reading the pointer. Once I realized such a pointer existed, the rest of the implementation was straightforward.

So there you have it. The Windows kernel sometimes uses a special pointer that stashes a reference counter in the lower bits. Something to watch out for when you're doing virtual machine introspection. Hopefully this blog post will save others some time.