ReactOS의 Handle Indexing 방법이 있는 코드다.미친감자님의 블로그에 핸들 테이블 관련 정보가 있길래 포스팅합니다. :-)
심심이는 프로세스 목록을 얻기 위해 아래의 두가지를 이용합니다.
- 스케쥴러 관련 정보
- 핸들 테이블 (PspCidTable)
아래 함수는 xp 에서 핸들 테이블을 이용해서 프로세스 목록을 구하는 함수입니다.
오래전에 PoC 형태로 작성했던 코드라.. 지저분 하고, 걸레스럽지만 뭐 잘 돌아가기때문에.. 심심이에서 그대로 쓰고 있죠. -_-;;
자세한 내용은
http://somma.egloos.com/2947301 를 참고하시면 되고요.
/** -----------------------------------------------------------------------
\brief
\param
\return
\code
\endcode
-------------------------------------------------------------------------*/
NTSTATUS __stdcall ScanXpHandleTable(IN PHANDLE_TABLE pPspCidTable,
/*IN DWORD NumberOfCpu, */
IN PPROC_MANAGER pPm)
{
ASSERT(NULL != pPspCidTable);
ASSERT(NULL != pPm);
if ( (NULL == pPspCidTable) || (NULL == pPm) )
{
SPDEBUG(DBG_GENERAL, DBG_ERR, "%s >> invalid parameter", __FUNCTION__);
return STATUS_INVALID_PARAMETER;
}
PXP_HANDLE_TABLE pXpHandleTable = (PXP_HANDLE_TABLE)pPspCidTable;
ULONG i = 0, j = 0, k = 0;
NTSTATUS status = STATUS_UNSUCCESSFUL;
// 31 2 1 0
// |--------------------------------|-|-|
// 상위 30 비트 : _HANDLE_TABLE_ENTRY 포인터
// 하위 2 비트 : 핸들 테이블 레벨 인덱스
//
// LevelIndex : 현재 시스템에 구성된 HANDLE_TABLE 레벨
// pEntryPtr : 현재 LevelIndex 에 맞는 첫번째 PHANDLE_TABLE_ENTRY
//
ULONG LevelIndex = pXpHandleTable->TableCode & TABLE_LEVEL_MASK;
ULONG pEntryPtr = pXpHandleTable->TableCode & ~TABLE_LEVEL_MASK;
// xp, 2003 의 경우 페이지가 수용하는 만큼의 HANDLE_TABLE_ENTRY -1 개를 생성하며
// x86 mmu 는 프로세스당 16M 의 핸들을 사용할 수 있게 한다.
// top level pointers = 32 개
// mid level pointers = 1024 개
// sub handle table = 512 개
//
ULONG top_level_count = 32;
ULONG mid_level_count = 1024;
ULONG sub_level_count = PAGE_SIZE / sizeof(HANDLE_TABLE_ENTRY);
DWORD Cid = 0;
PHANDLE_TABLE_ENTRY pEntry = NULL;
// xp 의 경우 _HANDLE_TABLE.TableCode 의 하위 2비트를 통해서
// 3 단계 _HANDLE_TABLE 을 구성한다.
//
switch (LevelIndex)
{
case 0 :
SPDEBUG(DBG_GENERAL, DBG_INFO, "handle table level index = 0", "");
for (i = 0; i < sub_level_count; i++)
{
pEntry = &((PHANDLE_TABLE_ENTRY)pEntryPtr)[i];
if ( ( NULL != pEntry ) && (NULL != pEntry->Object) )
{
// xp, 2003 은 _HANDLE_TABLE_ENTRY.Object 필드의 최하위 비트가 Lock 비트 이므로
// Lock 비트를 클리어시키면 _HANDLE_TABLE_ENTRY.Object 는 커널 오브젝트 포인터가 된다.
// 즉 이것은 _OBJECT_HEADER.Body 를 가리키는 포인터이다.
// 이때 _OBJECT_HEADER.Type 필드를 참조해서 Body 가 어떤 오브젝트인지 판단하며
// 만일 Process object 인 경우 Body 필드는 _EPROCESS 구조체의 시작 주소가 된다.
// 주의할 것은 _OBJECT_HEADER.Body 필드는 포인터가 아닌 실제 구조체란 것이다.
//
// ( (ULONG) Entry->Object & ~XP_TABLE_ENTRY_LOCK_BIT )
//
// 위 식은 실제 Object 를 가리키는 포인터 이므로 _OBJECT_HEADER 를 찾기 위해서는
//
// PVOID pObj = ( (ULONG) Entry->Object & ~XP_TABLE_ENTRY_LOCK_BIT ); // process (_EPROCESS) / thread (_ETHREAD)
//
// POBJECT_HEADER pObjHeader = (ULONG) pObj - (Body offset);
//
// BodyOffset 은 xp sp2 의 경우 0x18 이다.
//
// kd> dt _OBJECT_HEADER
// +0x000 PointerCount : Int4B
// +0x004 HandleCount : Int4B
// +0x004 NextToFree : Ptr32 Void
// +0x008 Type : Ptr32 _OBJECT_TYPE
// +0x00c NameInfoOffset : UChar
// +0x00d HandleInfoOffset : UChar
// +0x00e QuotaInfoOffset : UChar
// +0x00f Flags : UChar
// +0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION
// +0x010 QuotaBlockCharged : Ptr32 Void
// +0x014 SecurityDescriptor : Ptr32 Void
// +0x018 Body : _QUAD <<<<=======
//
//
PVOID pObjBody = (PVOID)( (ULONG) pEntry->Object & ~XP_TABLE_ENTRY_LOCK_BIT );
POBJECT_HEADER pObjHdr = OBJECT_TO_OBJECT_HEADER(pObjBody);
if (pObjHdr->Type == *PsProcessType)
{
PEPROCESS pEproc = (PEPROCESS) pObjBody;
Cid = *(ULONG*)((ULONG)pEproc + pPm->UniqueProcessId_offset);
//
// process list 에 추가
//
status = AddProcessAtDpcLevel(pPm, Cid, pEproc);
if (! NT_SUCCESS(status) )
{
SPDEBUG(DBG_GENERAL, DBG_ERR,
"AddProcessAtDpcLevel()");
continue;
}
}//if (pObjHdr->Type == *PsProcessType)
}
} // for
break;
case 1 :
SPDEBUG(DBG_GENERAL, DBG_INFO, "handle table level index = 1", "");
for (i = 0; i < mid_level_count; i++)
{
if (NULL != ( (PVOID *)pEntryPtr)[i] )
{
for (j = 0; j < sub_level_count; j++)
{
pEntry = &((PHANDLE_TABLE_ENTRY *)pEntryPtr)[i][j];
if ( (NULL != pEntry) && (NULL != pEntry->Object) )
{
PVOID pObjBody = (PVOID)( (ULONG) pEntry->Object & ~XP_TABLE_ENTRY_LOCK_BIT );
POBJECT_HEADER pObjHdr = OBJECT_TO_OBJECT_HEADER(pObjBody);
if (pObjHdr->Type == *PsProcessType)
{
PEPROCESS pEproc = (PEPROCESS) pObjBody;
Cid = *(ULONG*)((ULONG)pEproc + pPm->UniqueProcessId_offset);
//
// process list 에 추가
//
status = AddProcessAtDpcLevel(pPm, Cid, pEproc);
if (! NT_SUCCESS(status) )
{
SPDEBUG(DBG_GENERAL, DBG_ERR,
"AddProcessAtDpcLevel()");
continue;
}
}
} // if
}//for
}//if
}//for
break;
case 2 :
SPDEBUG(DBG_GENERAL, DBG_INFO, "handle table level index = 2", "");
for (i = 0; i < top_level_count; i++)
{
if (NULL != ( (PVOID *)pEntryPtr)[i] )
{
for (j = 0; j < mid_level_count; j++)
{
if (NULL != ((PVOID **)pEntryPtr)[i][j])
{
for (k = 0; k < sub_level_count; k++)
{
pEntry = &((PHANDLE_TABLE_ENTRY **)pEntryPtr)[i][j][k];
if ( (NULL != pEntry) && (NULL != pEntry->Object) )
{
PVOID pObjBody = (PVOID)( (ULONG) pEntry->Object & ~XP_TABLE_ENTRY_LOCK_BIT );
POBJECT_HEADER pObjHdr = OBJECT_TO_OBJECT_HEADER(pObjBody);
if (pObjHdr->Type == *PsProcessType)
{
PEPROCESS pEproc = (PEPROCESS) pObjBody;
Cid = *(ULONG*)((ULONG)pEproc + pPm->UniqueProcessId_offset);
//
// process list 에 추가
//
status = AddProcessAtDpcLevel(pPm, Cid, pEproc);
if (! NT_SUCCESS(status) )
{
SPDEBUG(DBG_GENERAL, DBG_ERR,
"AddProcessAtDpcLevel");
continue;
}
}
}
}
}
}//for
}//if
}//for
break;
default:
SPDEBUG(DBG_GENERAL, DBG_INFO,
"oops! handle table level index is over 3?");
break;
}
return STATUS_SUCCESS;
}