В статье Ms-Rem'а "Обнаружение скрытых процессов" параграф "Получение списка процессов путем сканирования PspCidTable" и в статье "3 метода работы с занятыми файлами" происходит обращение к PspCidTable без наложения блокировки. Правильно ли это? Если это не правльно, то как её поставить? В структуре _HANDLE_TABLE есть поле EX_PUSH_LOCK HandleTableLock[4] - почему то 4х элементный массив. Это блокировки на каждый индекс? Вообщем, если у кого-то есть какие-то соображения, буду признателен.
Нет, конечно не правильно. Какие именно операции над таблицей описателей интересуют? Разные операции требуют разных манипуляций над синхро-объектами. P.S. ответы на все заданные вопросы есть в WRK
интересует нахождение ETHREAD по ThreadId. конкретная задача своя реализация PsLookupThreadByThreadId для Windows XP. она не срабатывает в колбэке PsSetCreateThreadNotifyRoutine
А совет-то не плохой: если не умеем читать дизассемблерные листинги, то открываем реализацию nt!PspCreateThread() в WRK. И в ней видим, что вызов nt!PspCreateThreadNotifyRoutine–callback’ов происходит уже после вставки нового элемента в nt!PspCidTable (а иначе ядру будет неоткуда взять ThreadId). Трассировкой nt!PsLookupThreadByThreadId можно найти отличия ее от "своей реализации" и исправить ошибку. Это если присутствует академический интерес, если нужна реализация – просто копируем ее из WRK. Или "свою реализацию" выкладывайте - тут нет экстрасенсов, никто баг не найдет без дополнительных данных: исходники, анализ падения, если происходит.
ситуация обратная - стандартная реализация в Windows XP не срабатывает в колбэке. в блоге x64 я прочёл, что это происходит из-за не установленной к этому моменту маски доступа и совет реализовать сканирование таблицы PspCidTable самостоятельно вот код ::P_ETHREAD GetThreadHandleById( ::PHANDLE_TABLE HandleTable, ::ULONG ThreadId ) { ::ULONG TableCode = HandleTable->TableCode & ~TABLE_LEVEL_MASK; ::PHANDLE_TABLE_ENTRY HandleTableEntry; switch (HandleTable->TableCode & TABLE_LEVEL_MASK) { case 0: // one index level table for (int i = 0; i < 200; ++i) { HandleTableEntry = &reinterpret_cast< ::PHANDLE_TABLE_ENTRY>(TableCode); if (HandleTableEntry->Object) { ::P_ETHREAD EThread = reinterpret_cast< ::P_ETHREAD>( reinterpret_cast< ::ULONG>(HandleTableEntry->Object) & ~XP_TABLE_ENTRY_LOCK_BIT); if (reinterpret_cast< ::ULONG>(EThread->Cid.UniqueThread) == ThreadId) return EThread; } } break; case 1: // two index level table { for (int i = 0; i < 200; ++i) { if (reinterpret_cast< ::PVOID *>(TableCode)) { for (int j = 0; j < 200; ++j) { HandleTableEntry = &reinterpret_cast< ::PHANDLE_TABLE_ENTRY *>(TableCode)[j]; if (HandleTableEntry->Object) { ::P_ETHREAD EThread = reinterpret_cast< ::P_ETHREAD>( reinterpret_cast< ::ULONG>(HandleTableEntry->Object) & ~XP_TABLE_ENTRY_LOCK_BIT); if (reinterpret_cast< ::ULONG>(EThread->Cid.UniqueThread) == ThreadId) return EThread; } } } } } break; case 2: // three index level table { for (int i = 0; i < 200; ++i) { if (reinterpret_cast< ::PVOID *>(TableCode)) { for (int j = 0; j < 200; ++j) { if (reinterpret_cast< ::PVOID **>(TableCode)[j]) { for (int k = 0; k < 200; ++k) { HandleTableEntry = &reinterpret_cast< ::PHANDLE_TABLE_ENTRY **>(TableCode)[j][k]; if (HandleTableEntry->Object) { ::P_ETHREAD EThread = reinterpret_cast< ::P_ETHREAD>( reinterpret_cast< ::ULONG>(HandleTableEntry->Object) & ~XP_TABLE_ENTRY_LOCK_BIT); if (reinterpret_cast< ::ULONG>(EThread->Cid.UniqueThread) == ThreadId) return EThread; } } } } } } } break; default: ASSERT(!"Unexpected"); } return NULL; } NTSTATUS CProcessTracker::PsLookupThreadByThreadId( __in HANDLE ThreadId, __out PETHREAD *Thread ) { #if (NTDDI_VERSION == NTDDI_WINXP) if (!Thread) return STATUS_INVALID_PARAMETER_2; ::P_ETHREAD pEThread = GetThreadHandleById(m_pPspCidTable, reinterpret_cast< ::ULONG>(ThreadId)); if (!pEThread) return STATUS_UNSUCCESSFUL; // increase reference count ::POBJECT_HEADER pObjHeader = OBJECT_TO_OBJECT_HEADER(pEThread); ::InterlockedIncrement(&pObjHeader->PointerCount); *Thread = reinterpret_cast< ::PETHREAD>(pEThread); return STATUS_SUCCESS; #elif (NTDDI_VERSION >= NTDDI_VISTA) return ::PsLookupThreadByThreadId(ThreadId, Thread); #else error Not Implemented! #endif // NTDDI_VERSION } // это PspCreateThreadNotifyRoutine callback VOID CProcessTracker::ThreadNotifyRoutine( IN HANDLE ProcessId, IN HANDLE ThreadId, IN BOOLEAN Create) { CProcessTracker *pProcessTracker = GetProcessTracker(); ::ULONG pid = reinterpret_cast<ULONG>(ProcessId); ::ULONG tid = reinterpret_cast<ULONG>(ThreadId); if (pid != pProcessTracker->m_TrackedProcessId) return; if (!Create) return; ::NTSTATUS status(STATUS_SUCCESS); ::ExAcquireFastMutex(&pProcessTracker->m_OperationGuard); __try { // recheck thread tracking conditions if (pid != pProcessTracker->m_TrackedProcessId) __leave; ::PETHREAD pEthread; status = pProcessTracker->PsLookupThreadByThreadId(ThreadId, &pEthread); if (NT_ERROR(status)) { :bgPrint("Failed to lookup thread.\nProces %i thread %i\nMessage: %s\n", pid, tid, OsrNTStatusToString(status)); __leave; } __try { ::HANDLE hThread; status = ::ObOpenObjectByPointer( pEthread, OBJ_KERNEL_HANDLE, NULL, STANDARD_RIGHTS_ALL, NULL, KernelMode, &hThread ); if (NT_ERROR(status)) { :bgPrint("Unable to open thread.\nProces %i thread %i\nMessage: %s\n", pid, tid, OsrNTStatusToString(status)); __leave; } __try { if (pProcessTracker->IsThreadExcluded(tid)) { status = (*pProcessTracker->m_pfnNtResumeThread)(hThread, NULL); } else { status = (*pProcessTracker->m_pfnNtSuspendThread)(hThread, NULL); // ^^^ выходит со статусом STATUS_INVALID_HANDLE } if (NT_ERROR(status)) { :bgPrint("Unable to suspend/resume thread.\nProces %i thread %i\nMessage: %s\n", pid, tid, OsrNTStatusToString(status)); __leave; } } __finally { ::ZwClose(hThread); } } __finally { ::ObfDereferenceObject(pEthread); } } __finally { ::ExReleaseFastMutex(&pProcessTracker->m_OperationGuard); } } PS Опыта кернел программирования у меня немного, поэтому правильный совет сэкономит мне массу времени
Имея на руках ThreadId (читай: описатель из nt!PspCidTable) приведенный код еще раз линейно идет по содержимому таблицы nt!PspCidTable... Значение описателя само по себе содержит необходимые индексы в своей таблице! В приведенном листинге, функцию GetThreadHandleById нужно заменить на аналог nt!ExpLookupHandleTableEntry(). А если уж есть навыки поиска и использования не-экспортируемых символов ядра, то проще всего вызвать: Код (Text): PHANDLE_TABLE_ENTRY ExMapHandleToPointer ( __in PHANDLE_TABLE HandleTable, __in HANDLE Handle );