0

If I'm not wrong, A handle is an index inside a table maintained on per process basis.

For 64bit Windows, Each entry in this table is made up of 8 byte address to the kernel object + 4 byte of access mask making the entry 12 byte long. However as I understood, for alignment purpose each entry made 16 byte long.

But when you you look at handle opened by a process using process explorer, Value of handle are in multiple of 4. Shouldn't this be in multiple of 16 instead?

process explorer screen shot

Thomas Weller
  • 55,411
  • 20
  • 125
  • 222
  • [Why are kernel HANDLEs always a multiple of four?](https://devblogs.microsoft.com/oldnewthing/20050121-00/?p=36633) – IInspectable Jan 10 '22 at 16:11

2 Answers2

0

A Windows handle is just an index per se, it could be a multiple of 1 in principle. It has been probably more efficient to implent a word (16 bit value) alignment than the byte alignment you're implying.

phoebus
  • 44
  • 2
0

The lowest two bits of a kernel handle are called "tag bits" and are available for application use. This has nothing to do with the size of an entry in the handle table.

The comment in ntdef.h (in Include\10.0.x.x\shared) says:

//
// Low order two bits of a handle are ignored by the system and available
// for use by application code as tag bits.  The remaining bits are opaque
// and used to store a serial number and table index.
//

#define OBJ_HANDLE_TAGBITS  0x00000003L

My guess is that it's a similar misuse like using the most significant bit of 32 bit pointers as a boolean flag, which is why we have LAA (large address aware) and non-LAA applications.

You could (but should not) add 1, 2 or 3 to a HANDLE and it should not affect other Windows API methods. E.g. WaitForSingleObject():

#include <iostream>
#include <windows.h>
int main()
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));
    auto created = CreateProcess(L"C:\\Windows\\System32\\cmd.exe",
        nullptr, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi
    );
    if (created)
    {
        pi.hProcess = static_cast<byte*>(pi.hProcess) + 3;
        const auto result = WaitForSingleObject(pi.hProcess, INFINITE);
        if (result == 0)
            std::cout << "Completed!\n";
        else
            std::cout << "Failed!\n" << result << "\n";
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    }
    else
        std::cout << "Not created";
}
Thomas Weller
  • 55,411
  • 20
  • 125
  • 222