The term 'handle' in this text refers to handles to the internel kernel32 objects, which are usually cast as type HANDLE. These objects are process related objects like processes, threads, mutexes, semaphores, sockets, files etc. Basically any handle you can close with CloseHandle(). This does NOT include GDI objects, things like HPEN, HDC, HBITMAP etc, they have almost nothing to do with kernel32 objects.

A handle, within the context of a process, is supposedly (not documented by Microsoft) the index into the processes' handle table of the entry which contains the info about the object the handle references. The handle table is a DWORD representing the number of entries in the array followed by an array of handle entries. A handle entry is a DWORD representing the access flags for the entry followed by a pointer to the actual object in memory.

typedef struct _HANDLE_TABLE
{
        DWORD   cEntries;               // Max number of handles in table
        HANDLE_TABLE_ENTRY array[1];    // An array (number is given by cEntries)
} HANDLE_TABLE, *PHANDLE_TABLE;

typedef struct _HANDLE_TABLE_ENTRY
{
        DWORD   flags;      // Valid flags depend on what type of object this is
        PVOID   pObject;    // Pointer to the object that the handle refers to
} HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;

A process ID is actually a pointer to the process structure containing the process information, xor'd with an 'obsfucator'. There are several algorythms I've seen to get this value. I'm using my own which doesn't use any undocumented calls, by calling GetCurrentProcessId() in ring 3, passing that value to a VxD which calls VWIN32_GetCurrentProcessHandle (which returns the unobfuscated pointer to the current PDB) and xor'ing the two to get the 'obsfucator' which is returned to ring 3.

A reasonable method for obtaining this value that does not use a VxD (thanks dd!) is:

DWORD _dw = 0, _dwCurProcId = GetCurrentProcessId();
__asm {
	mov eax,dword ptr fs:[18h]  
	mov eax,dword ptr [eax+30h] 
	mov ebx,eax
	xor ebx,dword ptr [_dwCurProcId]
	mov dword ptr [_dw],ebx 
};
Unobsfucator = _dw;

A process database structure (PDB) contains all the info about an active process, and is in fact one of the standard 17 types of kernel32 objects.

typedef struct _PROCESS_DATABASE
{
        DWORD   Type;               // 00h KERNEL32 object type (5)
        DWORD   cReference;         // 04h Number of references to process
        DWORD   un1;                // 08h
        DWORD   someEvent;          // 0Ch An event object 
        DWORD   TerminationStatus;  // 10h Returned by GetExitCodeProcess
        DWORD   un2;                // 14h
        DWORD   DefaultHeap;        // 18h Address of the process heap
        DWORD   MemoryContext;      // 1Ch pointer to the process's context
        DWORD   flags;              // 20h
        DWORD   pPSP;               // 24h Linear address of PSP?
        WORD    PSPSelector;        // 28h
        WORD    MTEIndex;           // 2Ah
        WORD    cThreads;           // 2Ch
        WORD    cNotTermThreads;    // 2Eh
        WORD    un3;                // 30h
        WORD    cRing0Threads;      // 32h number of ring 0 threads
        HANDLE  HeapHandle;         // 34h Heap to allocate handle tables out of
                               //     This seems to always be the KERNEL32 heap
        DWORD   W16TDB;             // 38h Win16 Task Database selector
        DWORD   MemMapFiles;        // 3Ch memory mapped file list (?)
        PENVIRONMENT_DATABASE pEDB; // 40h Pointer to Environment Database
        PHANDLE_TABLE pHandleTable; // 44h Pointer to process handle table
        struct _PROCESS_DATABASE * ParentPDB;   // 48h Parent process database
        PMODREF MODREFlist;         // 4Ch Module reference list
        DWORD   ThreadList;         // 50h Threads in this process
        DWORD   DebuggeeCB;         // 54h Debuggee Context block?
        DWORD   LocalHeapFreeHead;  // 58h Head of free list in process heap
        DWORD   InitialRing0ID;     // 5Ch
        CRITICAL_SECTION    crst;   // 60h
        DWORD   un4[2];             // 78h
        DWORD   pConsole;           // 84h Pointer to console for process
        DWORD   tlsInUseBits1;      // 88h  // Represents TLS indices 0 - 31
        DWORD   tlsInUseBits2;      // 8Ch  // Represents TLS indices 32 - 63
        DWORD   ProcessDWORD;       // 90h
        struct _PROCESS_DATABASE * ProcessGroup;    // 94h
        PMODREF pExeMODREF;         // 98h pointer to EXE's MODREF
        DWORD   TopExcFilter;       // 9Ch Top Exception Filter?
        DWORD   BasePriority;       // A0h Base scheduling priority for process
        DWORD   HeapOwnList;        // A4h Head of the list of process heaps
        DWORD   HeapHandleBlockList;// A8h Pointer to head of heap handle block list
        DWORD   pSomeHeapPtr;       // ACh normally zero, but can a pointer to a
	                            // moveable handle block in the heap
        DWORD   pConsoleProvider;   // B0h Process that owns the console we're using?
        WORD    EnvironSelector;    // B4h Selector containing process environment
        WORD    ErrorMode;          // B6H SetErrorMode value (also thunks to Win16)
        DWORD   pevtLoadFinished;   // B8h Pointer to event LoadFinished?
        WORD    UTState;            // BCh
} PDB, *PPDB;

All kernel32 objects (basically, anything you can get a handle for) share a common header: The first DWORD represents the object type, and the second DWORD represents the usage count (the number of handles referencing the object). The types for the object types are defined as:

K32OBJ_SEMAPHORE            0x1
K32OBJ_EVENT                0x2
K32OBJ_MUTEX                0x3
K32OBJ_CRITICAL_SECTION     0x4
K32OBJ_PROCESS              0x5
K32OBJ_THREAD               0x6
K32OBJ_FILE                 0x7
K32OBJ_CHANGE               0x8
K32OBJ_CONSOLE              0x9
K32OBJ_SCREEN_BUFFER        0xA
K32OBJ_MEM_MAPPED_FILE      0xB
K32OBJ_SERIAL               0xC
K32OBJ_DEVICE_IOCTL         0xD
K32OBJ_PIPE                 0xE
K32OBJ_MAILSLOT             0xF
K32OBJ_TOOLHELP_SNAPSHOT    0x10
K32OBJ_SOCKET               0x11

To get the process ID of a process referenced by a process handle, I use GetCurrentProcessId() ^ Obsfucator to get a pointer to the current processes database structure. This structure contains a pointer at offset 44h which points to the handle table. All the objects begin with a DWORD indicating the type of the object, followed by a DWORD representing the usage count (the number of handles refering to this object). The handle should be an index into this array, and the process ID for the process refered to by the handle should be the pointer to the object xor'ed with Obsfucator. I'm fairly certain that these pointers (to the objects) are in shared memory, and handles in different processes can point to the same object in shared memory, and that there is one PDB object for each process, and that handles in different processes which refer to the same process will point to the same process structure.

On Win98, and on Win95 with some kernel upgrade, the format of the internal objects seems to have changed. Instead of the process object beginning with the DWORD 5 followed by the usage count, it seems to begin with a WORD (0x0006) followed by a WORD for the usage count, followed by a pointer to something I haven't identified.

typedef struct _newPDB
{
	WORD   Type;
	WORD   Usage;
	PVOID  SomePointer;
...

Most of the rest of the structure seems to be the same, but I think something may have changed with the handle table as well. It seems that a handle is no longer a straight index into the array, it is four times the array index. To tell which form of the kernel object I am looking at, I check the HIWORD of the Type DWORD for being 0. Since all the K32OBJ Type constants are well below 0xffff, the the HIWORD will always be 0 if it is set to one of these values, but in the new format the HIWORD represents the usage count, which will always be greater than 0. So, to get to a process ID for a handle in any given process, I use the following:

DWORD GetProcessID(HANDLE hProcess, DWORD pid)
{
        PPDB ppdb = NULL, ppdb2 = NULL;
        DWORD index;

		// check for special 'current process' value
	if ((DWORD)hProcess == 0x7fffffff)	return pid;	
		// initialize 'Obsfucator' by whatever method
        if (Unobsfucator == 0)   InitUnobsfucator();
        if (Unobsfucator == 0)   return 0;        

        ppdb = (PPDB)(pid ^ Unobsfucator);	// get pointer to PDB
	if (ppdb == NULL)	return 0;	// make sure not null pointer

		// check if new format or old format
        if (HIWORD(ppdb->Type) == 0) // old format
                index = (DWORD)hProcess;	// handle is straight index 
	else // new format
                index = (DWORD)hProcess/4;	// handle is index * 4 

		// make sure index is valid
        if (index > ppdb->pHandleTable->cEntries) return 0;	
		// make sure not empty entry
	if (ppdb->pHandleTable->array[index].pObject == NULL)	return 0; 
		// point ppdb2 to object
	ppdb2 = (PPDB)ppdb->pHandleTable->array[index].pObject;
		// check for process object
        if (LOWORD(ppdb2->Type) != LOWORD(ppdb->Type)) return 0;
	
	return (DWORD)ppdb2 ^ Unobsfucator;		
}
The new kernel32 object types that I've identified are:
Semaphore: 		0x01
Event:			0x02
Mutex:			0x03
	Critical section?
	Fiber?
Process:		0x06
Thread:			0x07
File:			0x08
Change notify:		0x09
Console in: 		0x0a
	New console related type?
Screen buffer:		0x0c
File mapping:		0x0d
Serial:			0x0e
VxD handle:		0x0f
Unnamed Pipe:		0x10
Mailslot:		0x11
Toolhelp Snapshot: 	0x12
Socket:			0x13
	Waitable Timer?
	Job?  (Win2k)
	Tape drive? (Win2k)

If you know anything about this change in the internal structure format such as what upgrade actually caused this change in the kernel, or any of the new formats, or anything else that may have changed also, please share the knowledge!