В этой статье я покажу вам, как найти подлинный процесс подсистемы Windows. Это полезно, например, когда вы пытаетесь получить список активных процессов (обратите внимание, таким способом можно найти только процессы, запущенные в подсистеме Windows).
и помимо Windows Subsystem POSIX и OS/2 существуют и другие подсистемы, которые, можно сказать, больше не поддерживаются), перечисляя структуры CSR_PROCESS , список которых находится в обработке КСРСС .
Самый простой способ найти процесс КСРСС это использование PSActiveProcessHead (это указатель на начало двусвязного списка, который можно найти в любой структуре ЭПРОЦЕСС ) и найдите первый процесс с именем «csrss.exe» , ну а поскольку эта функция используется в основном в модулях IPD/Anti-Rootkit, то этот алгоритм ненадежен (фактически он ненадежен в любой ситуации).
Например, руткит может измениться (с помощью ДКОМ ) список процессов и избежать обнаружения.
Поэтому я хочу показать вам лучший вариант. Во время загрузки системы, после запуска процесса КСРСС , среди многих задач инициализации, КСРСС также создает АЛПК порт называется АпиПорт , с помощью КСРСС реализует собственный API. Сразу после создания порта АпиПорт , КСРСС создает поток CsrApiRequestThread который затем вызывает NtAlpcSendWaitReceivePort и передает ему дескриптор порта в качестве параметра для ожидания порта (ожидания связи клиентов).
Как вы можете видеть на картинке, АпиПорт находится в каталоге объектов \Окна .
Поскольку для каждого последующего сеанса создается новый процесс КСРСС , Что АпиПорт для каждой сессии уже будут созданы в каталоге \Сессии\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.
Чтобы понять, что нам нужно сделать на втором этапе, нам сначала нужно немного глубже погрузиться в Диспетчер объектов.
Каждый объект, созданный Диспетчером объектов, имеет следующую структуру:
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))].
/******/
Кроме того, дополнительные разъемы расположены строго так, как показано на рисунке.
Оказывается, проблема в том, что Обпинформасктооффсет не экспортируется, это означает, что либо мы должны получить его с помощью поиска по шаблону, либо мы можем реализовать собственную функцию для расчета смещения на основе уже имеющихся у нас знаний (есть еще 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: великий и ужасный #безопасность #информационная безопасность #Системное программирование
-
Ui/Ux Дизайнер И Маркетолог Ищет Работу
19 Oct, 24 -
Облачная Касса, Мой Скромный Опыт
19 Oct, 24