Получение Подлинного Процесса Подсистемы Windows (Csrss.exe)

В этой статье я покажу вам, как найти подлинный процесс подсистемы Windows. Это полезно, например, когда вы пытаетесь получить список активных процессов (обратите внимание, таким способом можно найти только процессы, запущенные в подсистеме Windows).

и помимо Windows Subsystem POSIX и OS/2 существуют и другие подсистемы, которые, можно сказать, больше не поддерживаются), перечисляя структуры CSR_PROCESS , список которых находится в обработке КСРСС .

Самый простой способ найти процесс КСРСС это использование PSActiveProcessHead (это указатель на начало двусвязного списка, который можно найти в любой структуре ЭПРОЦЕСС ) и найдите первый процесс с именем «csrss.exe» , ну а поскольку эта функция используется в основном в модулях IPD/Anti-Rootkit, то этот алгоритм ненадежен (фактически он ненадежен в любой ситуации).

Например, руткит может измениться (с помощью ДКОМ ) список процессов и избежать обнаружения.

Поэтому я хочу показать вам лучший вариант. Во время загрузки системы, после запуска процесса КСРСС , среди многих задач инициализации, КСРСС также создает АЛПК порт называется АпиПорт , с помощью КСРСС реализует собственный API. Сразу после создания порта АпиПорт , КСРСС создает поток CsrApiRequestThread который затем вызывает NtAlpcSendWaitReceivePort и передает ему дескриптор порта в качестве параметра для ожидания порта (ожидания связи клиентов).



Получение подлинного процесса подсистемы Windows (csrss.exe)

Как вы можете видеть на картинке, АпиПорт находится в каталоге объектов \Окна .

Поскольку для каждого последующего сеанса создается новый процесс КСРСС , Что АпиПорт для каждой сессии уже будут созданы в каталоге \Сессии\N\Windows .

Одно из характерных архитектурных решений при застройке АЛПК модель заключалась в том, что только процесс, создающий порт сервера, может получить его дескриптор и, естественно, он получает его, вызывая Нткреатепорт .

Таким образом, единственный процесс, имеющий открытый дескриптор АпиПорт является КСРСС .

После всех этих обсуждений можно описать общие шаги по выявлению истинного процесса.

КСРСС .

1. Каким-то образом получить объект АпиПорт .

2. Получите процесс с открытым дескриптором порта.

ШАГ 1. Как я уже говорил, процедура ObOpenObjectByName не будет работать для портов сервера АЛПК (Следует отметить, что все остальные типы портов безымянны).

Если вы попытаетесь использовать 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 );

Очевидно, что последний параметр получает адрес объекта.

Пример кода для получения объекта АпиПорт :

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

Примечание! Лпкпортобжекттипе , ЛпкВайтаблеПортОбжекттипе , АлпкПортОбжектТип - все они указывают на одну и ту же структуру OBJECT_TYPE , поэтому не имеет значения, какой из них использовать ШАГ 2. Чтобы понять, что нам нужно сделать на втором этапе, нам сначала нужно немного глубже погрузиться в Диспетчер объектов.

Каждый объект, созданный Диспетчером объектов, имеет следующую структуру:

Получение подлинного процесса подсистемы Windows (csrss.exe)

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-> Информационная маска .

В целом, Инфомаска это битовая маска, биты которой указывают на наличие дополнительных заголовков.

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 Инфомаска также используется для расчета смещения в массиве (не экспортируется) Обпинформасктооффсет , который используется для получения смещения желаемого дополнительного заголовка относительно начала тела объекта.

Код, моделирующий алгоритм расчета смещения, выглядит следующим образом:

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

/******/

Кроме того, дополнительные разъемы расположены строго так, как показано на рисунке.



Получение подлинного процесса подсистемы Windows (csrss.exe)

Оказывается, проблема в том, что Обпинформасктооффсет не экспортируется, это означает, что либо мы должны получить его с помощью поиска по шаблону, либо мы можем реализовать собственную функцию для расчета смещения на основе уже имеющихся у нас знаний (есть еще 3 способа: реализовать собственный массив Обпинформасктооффсет ).

Я выбрал второй метод.

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 Содержит один элемент, который является объединением, а объединение состоит из двух полей.

HandleCountDataBase , Однократная (_OBJECT_HEADER-> Флаг определяет, какое поле использовать).

В частности OB_FLAG_SINGLE_HANDLE_ENTRY Флаг (0x40) указывает, что поле должно использоваться.

Однократная , а в остальных случаях используется HandleCountDataBase (обычно ALPC всегда используется для объектов порта HandleCountDataBase ).

Для портов сервера ALPC HandleCountDataBase всегда содержит два элемента (т.е.

Количество записей == 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-> Процесс владельца , (указатель на ALPC_PORT мы получаем от ObReferenceObjectByName ) просто на момент написания статьи мне нужен был способ получить список процессов, имеющих открытые хэндлы объекта и я даже не удосужился подробно рассмотреть структуру ALPC_PORT , тем не менее, прошу не бить меня по почкам, потому что.

Я думаю, что материал о Объект и заголовки объектов все еще был полезен.

Вот и все! ССЫЛКИ Заголовки объектов Windows 7 Теги: #драйвер #Windows 7 #режим ядра #csrss #C: великий и ужасный #безопасность #информационная безопасность #Системное программирование

Вместе с данным постом часто просматривают: