正規のWindowsサブシステム(csrss.exe)プロセスを取得する

この記事では、Windowsサブシステムの本物のプロセスを見つける方法を示します。たとえば、アクティブなプロセスのリストを取得しようとするときに便利です(この方法では、Windowsサブシステムで動作するプロセスのみを見つけることができますが、Windowsサブシステムに加えてサブシステムもありますPOSIXおよびOS / 2(これらはサポートされなくなったと言われるかもしれません)、 CSRSSの処理中のリストであるCSR_PROCESS構造をリストします。 CSRSSプロセスを見つける最も簡単な方法は、 PsActiveProcessHead (これは任意のEPROCESS構造にある二重リンクリストの最上部へのポインターです)を使用し、 「csrss.exe」と呼ばれる最初のプロセスを検索することです。 -ルートキットモジュールの場合、このアルゴリズムは信頼できません(実際、どのような状況でも信頼できません)。 たとえば、ルートキットはプロセスのリストを( DKOMを使用して)変更し、検出を回避できます。 したがって、より良いオプションを紹介したいと思います。



システムのブート中、 CSRSSプロセスを開始した後、多くの初期化タスクの中で、 CSRSSApiPortと呼ばれるALPCポートも作成します。これにより、 CSRSSはそのAPIを実装します。 ApiPortポートを作成した直後に、 CSRSSCsrApiRequestThreadスレッドを作成し、次にNtAlpcSendWaitReceivePortを呼び出して、ポートで待機するためのパラメーターとしてポートハンドルを渡します(クライアントが通信するのを待機します)。





図からわかるようにApiPort\ Windowsオブジェクトディレクトリの下にあります。 後続のセッションごとに新しいCSRSSプロセスが作成されるため、各セッションのApiPort\ Sessions \ N \ Windowsディレクトリに既に作成されます

ALPCモデルを開発する際の明確なアーキテクチャ上の決定の1つは、サーバーポート(サーバーポート)を作成するプロセスのみがハンドルを受け取ることができ、 NtCreatePortを呼び出すことでそれを受け取ることでした 。 したがって、 ApiPortにオープン記述子を持つ唯一のプロセスはCSRSSです。



これらすべての議論の後、真のCSRSSプロセスを識別するための一般的な手順を説明できます。

1.どういうわけかApiPortオブジェクトを取得します。

2.ポートへの開いたハンドルを持つプロセスを取得します。



ステップ1。



すでに述べたように、 ObOpenObjectByNameプロシージャはALPCサーバーポートでは機能しません(他のすべてのタイプのポートは名前がないことに注意する価値があります)。 ALPCサーバーポートObOpenObjectByNameを使用しようとすると、エラーコードSTATUS_NOT_IMPLEMENTEDが表示されます。 しかし、実際には私たちを助けることができる関数があります: ObReferenceObjectByNameこの関数は文書化されていません、そのプロトタイプがあります:

NTKERNELAPI NTSTATUS ObReferenceObjectByName( __in PUNICODE_STRING ObjectName, __in ULONG Attributes, __in_opt PACCESS_STATE AccessState, __in_opt ACCESS_MASK DesiredAccess, __in POBJECT_TYPE ObjectType, __in KPROCESSOR_MODE AccessMode, __inout_opt PVOID ParseContext, __out PVOID *Object );
      
      







明らかに、最後のパラメーターはオブジェクトのアドレスを取得します。

ApiPortオブジェクトを取得するサンプルコード:

 UNICODE_STRING apiport; PVOID pApiPort; NTSTATUS ret; RtlInitUnicodeString(&apiport,L"\\Windows\\ApiPort"); ret = ObReferenceObjectByName(&apiport,0,0,GENERIC_READ,*LpcPortObjectType,KernelMode,NULL,&pApiPort); DbgPrint("ObOpenObjectByName returned: %x\nOBJECT: %p",ret,pApiPort); if(!NT_SUCCESS(ret)) { //cleanup and exit } //Do some work.... ObDereferenceObject(pApiPort);
      
      







注!

LpcPortObjectTypeLpcWaitablePortObjectTypeAlpcPortObjectType-これらはすべて同じOBJECT_TYPE構造体を指しているため、どちらを使用するかは問題ではありません



ステップ2。

2番目のステップで何をする必要があるかを理解するには、最初にObject Managerの調査に少し飛び込む必要があります。

Object Managerによって作成された各オブジェクトの構造は次のとおりです。





オブジェクトヘッダー(OBJECT_HEADERS)は、「オブジェクトオプションヘッダー」と「オブジェクトヘッダー」で構成されます。オブジェクトヘッダーは、オブジェクトオプションヘッダーの直後にあります。



1.オブジェクトヘッダーは、_OBJECT_HEADER構造で表されます。

2.追加のオブジェクト見出しは、次の構造で表されます。

_OBJECT_HEADER_CREATOR_INFO

_OBJECT_HEADER_NAME_INFO

_OBJECT_HEADER_HANDLE_INFO

_OBJECT_HEADER_QUOTA_INFO

_OBJECT_HEADER_PROCESS_INFO



どの追加ヘッダーが存在するかを判断するには、 _OBJECT_HEADER-> InfoMask構造体フィールドを使用します。 一般に、 InfoMaskは、ビットが追加ヘッダーの存在を検出するビットマスクです。



0x1 _OBJECT_HEADER_CREATOR_INFO

0x2 _OBJECT_HEADER_NAME_INFO

0x4 _OBJECT_HEADER_HANDLE_INFO

0x8 _OBJECT_HEADER_QUOTA_INFO

0x10 _OBJECT_HEADER_PROCESS_INFO



InfoMaskは 、オブジェクトボディの先頭からの相対的な目的の追加ヘッダーのオフセットを取得するために使用されるObpInfoMaskToOffset配列(エクスポートされない)でオフセットを計算するためにも使用されます。

 /******/ BYTE HeaderOffset = ObpInfoMaskToOffset[_OBJECT_HEADER->InfoMask & (HeaderBit | (HeaderBit-1))]. /******/
      
      





さらに、追加のヘッダーは、示されているとおりに厳密に配置されます。





問題は、 ObpInfoMaskToOffsetがエクスポートされないことです。つまり、パターン検索を使用して取得するか、すでに持っている知識に基づいて変位を計算する独自の関数を実装することができます(さらに3つの方法があります:独自の配列ObpInfoMaskToOffsetを実装します)。



2番目の方法を選択しました。

 enum OBJ_HEADER_INFO_FLAG { HeaderCreatorInfoFlag = 0x1, HeaderNameInfoFlag = 0x2, HeaderHandleInfoFlag= 0x4, HeaderQuotaInfoFlag= 0x8, HeaderProcessInfoFlag= 0x10 }; BYTE GetObjectHeaderOffset( BYTE InfoMask,OBJ_HEADER_INFO_FLAG Flag) { BYTE OffsetMask,HeaderOffset=0; if( (InfoMask & Flag) == 0 ) return 0; OffsetMask = InfoMask & ( Flag | (Flag - 1) ); if((OffsetMask & HeaderCreatorInfoFlag) != 0) HeaderOffset += (BYTE)sizeof(OBJECT_HEADER_CREATOR_INFO); if((OffsetMask & HeaderNameInfoFlag) != 0) HeaderOffset += (BYTE)sizeof(OBJECT_HEADER_NAME_INFO); if((OffsetMask & HeaderHandleInfoFlag) != 0) HeaderOffset += (BYTE)sizeof(OBJECT_HEADER_HANDLE_INFO); if((OffsetMask & HeaderQuotaInfoFlag) != 0) HeaderOffset += (BYTE)sizeof(OBJECT_HEADER_QUOTA_INFO); if((OffsetMask & HeaderProcessInfoFlag) != 0) HeaderOffset += (BYTE)sizeof(OBJECT_HEADER_PROCESS_INFO); return HeaderOffset; } POBJECT_HEADER_HANDLE_INFO GetObjectHeaderHandleInfo(POBJECT_HEADER pObjHeader) { //DbgPrint("->GetObjectHeaderHandleInfo pObjHeader: %p",pObjHeader); BYTE HeaderOffset = GetObjectHeaderOffset(pObjHeader->InfoMask,HeaderHandleInfoFlag); if(HeaderOffset == 0) return NULL; //DbgPrint("->GetObjectHeaderHandleInfo HeaderOffset: %d",HeaderOffset); return (POBJECT_HEADER_HANDLE_INFO)((ULONGLONG)pObjHeader-(ULONGLONG)HeaderOffset); }
      
      





ご覧のとおり、 GetObjectHeaderHandleInfo()も実装しましたこれは次のような_OBJECT_HEADER_HANDLE_INFO構造が必要であることを示唆するものです。

 typedef struct _OBJECT_HANDLE_COUNT_ENTRY // 3 elements, 0x10 bytes (sizeof) { /*0x000*/ PEPROCESS Process; struct // 2 elements, 0x4 bytes (sizeof) { /*0x008*/ ULONG32 HandleCount : 24; // 0 BitPosition /*0x008*/ ULONG32 LockCount : 8; // 24 BitPosition }; ULONG32 Reserved; }OBJECT_HANDLE_COUNT_ENTRY, *POBJECT_HANDLE_COUNT_ENTRY; typedef struct _OBJECT_HANDLE_COUNT_DATABASE // 2 elements, 0x18 bytes (sizeof) { /*0x000*/ ULONG32 CountEntries; /*0x004*/ UINT8 Reserved[0x4]; /*0x008*/ struct _OBJECT_HANDLE_COUNT_ENTRY HandleCountEntries[1]; }OBJECT_HANDLE_COUNT_DATABASE, *POBJECT_HANDLE_COUNT_DATABASE; typedef struct _OBJECT_HEADER_HANDLE_INFO // 2 elements, 0x10 bytes (sizeof) { union // 2 elements, 0x10 bytes (sizeof) { /*0x000*/ struct _OBJECT_HANDLE_COUNT_DATABASE* HandleCountDataBase; /*0x000*/ struct _OBJECT_HANDLE_COUNT_ENTRY SingleEntry; // 3 elements, 0x10 bytes (sizeof) }; }OBJECT_HEADER_HANDLE_INFO, *POBJECT_HEADER_HANDLE_INFO;
      
      







_OBJECT_HEADER_HANDLE_INFOには 1つの要素が含まれます。これはunionであり、unionは2つのフィールドHandleCountDataBaseSingleEntryで構成されます(_OBJECT_HEADER-> Flagは使用するフィールドを決定します)。 特に、 OB_FLAG_SINGLE_HANDLE_ENTRY (0x40)フラグは、SingleEntryフィールドを使用する必要があることを決定し、他の場合には、 HandleCountDataBaseを使用します(原則として、 ALPCポートオブジェクトには常にHandleCountDataBaseが使用されます).ALPCサーバーポートの場合、 HandleCountDataBaseには常に2つの要素が含まれます= )および2番目の要素は常にゼロになります。



コードの最後の部分は次のとおりです。

  UNICODE_STRING apiport; POBJECT_HEADER pApiPortHeader; POBJECT_HEADER_HANDLE_INFO pHandleInfo; PVOID pApiPort; NTSTATUS ret; PEPROCESS procCSRSS; RtlInitUnicodeString(&apiport,L"\\Windows\\ApiPort"); ret = ObReferenceObjectByName(&apiport,0,0,GENERIC_READ,*LpcPortObjectType,KernelMode,NULL,&pApiPort); DbgPrint("ObOpenObjectByName returned: %x\nOBJECT: %p \nIndex: %d",ret,pApiPort,(*LpcPortObjectType)->Index); if(!NT_SUCCESS(ret)) DbgPrint("Can not reference ApiPort: %x",ret); /*Get the object header form object pointer ---------OBJECT_HEADER--------- | | | | | | /0x30/_________________________ | QUAD(OBJECT) | ............................... */ pApiPortHeader = OBJECT_TO_OBJECT_HEADER(pApiPort); pHandleInfo = GetObjectHeaderHandleInfo(pApiPortHeader); DbgPrint("Handle Info:%p\nOBject:%p\nSIZEOF:%d",pHandleInfo,pApiPortHeader,sizeof(OBJECT_HEADER_NAME_INFO)); if(pHandleInfo != NULL) { if(pApiPortHeader->Flags & OB_FLAG_SINGLE_HANDLE_ENTRY) procCSRSS = pHandleInfo->SingleEntry.Process; else procCSRSS = pHandleInfo->HandleCountDataBase->HandleCountEntries[0].Process; DbgPrint("CSRSS: %p",procCSRSS); } else { DbgPrint("Can not obtain Handle Info Header for ApiPort"); } ObDereferenceObject(pApiPort);
      
      





オブジェクトとオブジェクトヘッダーに関するこの大騒ぎはすべて、フィールドALPC_PORT-> OwnerProcessを使用することで回避できます( ObReferenceObjectByNameからALPC_PORTへのポインターを取得します)。 ALPC_PORTの構造を詳細に調べるのは面倒ですが、腎臓にぶつからないでください。 オブジェクトとオブジェクトヘッダーに関する資料はまだ有用だと思います。



以上です!



リンク集

Windows 7オブジェクトヘッダー



All Articles