PspCidTable 을 이용한 프로세스 목록 구하기 예제 심심이

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;
}


트랙백

이 글과 관련된 글 쓰기 (트랙백 보내기)
TrackbackURL : http://somma.egloos.com/tb/3678104 [도움말]

덧글

  • xeraph 2008/03/27 14:07 # 답글

    for if for if for if if if --;;;
  • somma 2008/03/27 14:58 # 답글

    ㅋㅋㅋㅋㅋ . 상당히 걸래스럽죠? :-)
  • 몰라몰라 2008/03/27 16:40 # 삭제 답글

    감사합니다~. 필요한 코드였는데 공개해주시다니.. ^^
    그런데 소스를 copy & paste 했더니 한 줄로 복사된다는..
    깔끔하게 복사하는 방법있나요? ^^
  • somma 2008/03/27 17:13 # 답글

    몰라몰라 / 읔... 그렇군요. -_-;;
    어떻게 해야 하는지 모르겠네요..
  • 미친감자 2008/03/27 22:16 # 답글

    허거덕...감사합니다.^^ 잘쓰겠습니다.
  • Dual 2008/03/28 00:21 # 삭제 답글

    이런 유용한 정보를 ' 0 '!ㅎㅎㅎ
  • noname 2008/03/28 00:24 # 답글

    ... <-- 왔다간 흔적
  • qwer 2008/03/28 08:11 # 삭제 답글

    감사합니다.
    조만간 공개할 예정이라던 코드가 드디어 공개되는군요 ㅋㅋㅋ
  • 홍가이버 2008/03/28 08:59 # 삭제 답글

    잘보고 갑니다. ^^
  • somma 2008/03/28 10:10 # 삭제 답글

    미친감자 / 별말씀을요..
    noname / 하하... 흔적 확인했습니다. ^^
    qwer/ 엊그제 사무실 놀러오셨던 분인가보군여.. ^^
    홍가이버 / 감사합니다.
  • Woof 2008/03/28 10:57 # 삭제 답글

    PspCidTable 구하는게 더.. :|
    핸들을 뒤지는건 W2K용하고 그 이후용으로 나누고 거기서 layer가 없거나 마지막 layer 뒤지는 부분을 함수로 분리했더니 그나마 깨끗해지던데.
  • kkamagui 2008/03/29 13:54 # 삭제 답글

    와우~ 좋은 글 잘 보고 갑니다. ^^)/~
    소마님의 포스팅은 실용적인(?) 글이 많은 것 같습니다. ^^
  • somma 2008/04/01 10:17 # 삭제 답글

    kkamagui / 감사합니다. 요즘 멀티 코어 관련 글들 잘 보고 있습니다. (어렵더군요. -_-) 잘 정리해서 계속 알려주세요. ^^
  • 미친감자 2008/04/05 21:14 # 답글

    음...세미나때..사용좀 해도 될까요???
  • somma 2008/04/07 10:44 # 삭제 답글

    미친감자 / 네 쓰셔도 돼요. ^^
  • 미친감자 2008/06/10 17:55 # 답글

    [질문]
    PVOID pObjBody = (PVOID)( (ULONG) pEntry->Object & ~XP_TABLE_ENTRY_LOCK_BIT ); POBJECT_HEADER pObjHdr = OBJECT_TO_OBJECT_HEADER(pObjBody);

    음 그러니까.. pEntry->Object 이 값이 body가 아니라 object header값이 아닌가요?
    아님... PspCidTable은 틀린건가요???

  • somma 2008/06/12 09:58 #

    body 값이 맞습니다. OBJECT_TO_OBJECT_HEADER() 매크로는 header 에서 body 까지의 offset 으로 header 의 위치를 찾는것이니까요. ^^
  • 학생 2011/11/21 02:07 # 삭제 답글

    인터넷 강의 녹화를 하려면 어떻게 해야 할까 고민하다 드라이버 개발에 입문하게 된 학생입니다.

    어쩌다 보니 여기 까지 오게 되었는데요 너무 어렵네요....

    이 소스는 드라이버 파일로 만들어서 돌려야 하는 그런건가요?
  • somma 2011/11/22 01:19 # 삭제

    네 드라이버 코드입니다.
  • 날아라통닥 2012/02/08 16:52 # 답글

    드라이버 개발 입문자인데요
    드라이버 코드는 AddDevice함수, PnpDispatch함수, Driver Unload함수가 필요하다고 이해하고있는데용
    여기 보여지는 함수는 이런거 필요없이 Driver Entry에서 그냥 쓰는 함수인가요?
    그리구 헤더파일은 ntddk.h말구 더 필요한게 있나요?
    아 어렵네용..ㅜㅜ 결정적으로 이 코드를 동작시킬려면 어떻게 해야하나요? 그냥 빌드하니깐 안되던데용..ㅜㅜ
  • somma 2012/02/14 13:48 # 삭제

    안녕하세요. 위에 있는 코드는 그냥 커널모드 코드 조각입니다.
    저 코드가 실행되려면 완전히 실행가능하게 만들어진 드라이버에서 호출해야 하는거죠.
    말씀하신것처럼.. Dispatch 함수 같은 곳에서 호출하시거나 DriverEntry 에서 호출하시면 됩니다.
    아.. DriverEntry 에서 호출하게 되면 문제가 생길지도..

    >> 아 어렵네용..ㅜㅜ 결정적으로 이 코드를 동작시킬려면 어떻게 해야하나요? 그냥 빌드하니깐 안되던데용..ㅜㅜ
    제 블로그에 있는 드라이버 개발 관련 포스팅을 참고하시거나 CFIX 같은 커널모드 드라이버 테스트프레임워크를 이용하시는것도 좋을것 같습니다.
    질문내용이 한줄이지만 답변은 너무 방대하기 때문에 곤란하네요 ^______^
댓글 입력 영역