Sunday, September 2, 2007

Handle Format

A couple of posts ago I talked about how to get the list of all open handles. When I started playing with that it did not work at the first try, so I had to use the brute force method to get all the handles in the current process. The code was doing a loop like this:


for (LONG_PTR h = 0; h < 0xFFFF; ++h) {
HANDLE handle = (HANDLE)(h);
DoSomething(handle);
}


The result was unexpected to me. All handles in the process were duplicated 4 times. It looks like the lowest 2 bits in the handle value are ignored.

If the handle to a file is 0x7C then you can use 0x7C, 0x7D, 0x7E or 0x7F to access the file.

Is this documented somewhere?

Updated on 9/3/2007:
I was talking with Ivanlef0u about this and my assumption that the last 2 bits are ignored is right. He showed me the disassembly of the function to lookup the object in the handle table and it looks like this:


ExpLookupHandleTableEntry
mov edi, edi
push ebp
mov ebp, esp
and [ebp+Handle], 0FFFFFFFCh ; Unset the last 2 bits
mov eax, [ebp+Handle]
mov ecx, [ebp+HandleTable]
mov edx, [ebp+Handle]
shr eax, 2 ; Shift right the last 2 bits
cmp edx, [ecx+38h] ; HandleTable->NextHandleNeedingPool
jnb loc_49DDF8


Thanks Ivanlef0u.

1 comment:

Alex Ionescu said...

The last two bits are *not* ignored! They're reserved for per-object use and have different meanings.

For example, if the SectionHandle given to NtCreateProcess is ORed by 1, then that adds the "Breakaway from job" flag.

If the DebugHandle given to NtCreateProcess is ORed by 1, then that adds the "No debug inherit" flag.

If the EventHandle inside lpOverlapped given to GetCompletionQueueStatus is ORed by 1 that means not to generate a notification for that I/O.

I think that's about all the hacks I can remember.