2016-08-11 7 views
-2

Отказ от ответственности:STATUS_ACCESS_DENIED на призыв NtQueryMutant

Единственная причина для этого вопроса и ниже кода, существует внешний компонент, используемый в моем приложении, который не может быть заменен, по крайней мере в ближайшем будущем. Логика этого компонента перехватывает вызовы WinAPI из приложения и выполняет различные задачи на основе этих вызовов.

Одна из особенностей, которую выполняет компонент, она создает мьютекс для каждого потока, инициализированного внутри приложения. Однако он не закрывает мьютексы, что приводит к утечке ручек.

Поэтому, чтобы предотвратить утечку и потому что у меня нет доступа к исходному коду компонента, мне приходится изобретать уродливые обходные пути и использовать esoteric API.

Конец оговорки

Я пытаюсь проверить состояние мутексов в моем приложении. Чтобы сделать это без изменения состояния каждого из объектов, которые я проверяю, я должен использовать метод NtQueryMutant от ntdll.dll.

Основываясь на примерах here и here я написал следующий код для достижения этой цели:

enum MUTANT_INFORMATION_CLASS 
{ 
    MutantBasicInformation 
}; 

struct MUTANT_BASIC_INFORMATION { 
    LONG CurrentCount; 
    BOOLEAN OwnedByCaller; 
    BOOLEAN AbandonedState; 
}; 

typedef NTSTATUS(WINAPI*QueryMutexHandler)(HANDLE, MUTANT_INFORMATION_CLASS, PVOID, ULONG, PULONG); 

//somewhere in the code: 
QueryMutexHandler queryMutex = reinterpret_cast<QueryMutexHandler>(GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQueryMutant")); 
MUTANT_BASIC_INFORMATION mutantInfo; 
NTSTATUS status = queryMutex(objectHandleCopy, MutantBasicInformation, &mutantInfo, sizeof(MUTANT_BASIC_INFORMATION), nullptr); 
if (NT_SUCCESS(status)) 
{ 
    //never arriving here 
} 

status я получаю здесь всегда -1073741790 (0xFFFF FFFF C000 0022) который, кроме того отрицательное число , выглядит точно так же, как STATUS_ACCESS_DENIED.

Это очень странно, потому что ранее в коде я использую как NtQuerySystemInformation, так и NtQueryObject без проблем.

Дополнительная информация: моя ОС - Windows 7 SP1, мьютексы, которые я пытаюсь выполнить, относятся к процессу, с которого я выполняю запрос.

+1

Зачем вам нужно, чтобы проверить состояние мьютекса? Проверка перед использованием - отличный способ представить очень распространенные ошибки состояния гонки. – andlabs

+0

@andlabs Это грязный обходной хак для предотвращения утечек ручек во внешнем компоненте, используемом приложением. – galenus

+1

вы не показываете, как вы открываете мутант. для запроса вам нужен доступ MUTANT_QUERY_STATE в дескрипторе. быстрее всего у вас нет этого доступа. и NtQueryObject здесь не связаны друг с другом, поскольку для него не требуются права доступа к дескриптору. NtQuerySystemInformation - совсем не связано – RbMm

ответ

2

для эффективного испытания. Мутант вам нужен, он ручка и маска доступа. вы можете получить его от SYSTEM_HANDLE_INFORMATION_EX. если у нас уже есть MUTANT_QUERY_STATE - может направить запрос, если нет - нужно не возобновлять ручку с MUTANT_QUERY_STATE

NTSTATUS QueryMutant(HANDLE hMutant, ULONG GrantedAccess, MUTANT_BASIC_INFORMATION* pmbi) 
{ 
    if (GrantedAccess & MUTANT_QUERY_STATE) 
    { 
     return ZwQueryMutant(hMutant, MutantBasicInformation, pmbi, sizeof(MUTANT_BASIC_INFORMATION), 0); 
    } 

    NTSTATUS status = ZwDuplicateObject(NtCurrentProcess(), hMutant, NtCurrentProcess(),&hMutant, 
     MUTANT_QUERY_STATE, 0, 0); 

    if (0 <= status) 
    { 
     status = ZwQueryMutant(hMutant, MutantBasicInformation, pmbi, sizeof(MUTANT_BASIC_INFORMATION), 0); 
     ZwClose(hMutant); 
    } 

    return status; 
} 

и вам не нужно все время использовать NtQueryObject для детерминированного типа ручки. вы можете использовать SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX.ObjectTypeIndex. для получения OBJECT_TYPE_INFORMATION этим индексом. для этого вам нужно только один раз вызвать ZwQueryObject(0, ObjectAllTypeInformation,) при запуске, но существует проблема, как конвертировать SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX.ObjectTypeIndex в индекс массива (без басов). начать с win8.1 «OBJECT_TYPE_INFORMATION.TypeIndex» справедливо и матч SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX.ObjectTypeIndex, но для ранней версии - вам нужно получить один раз SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX.ObjectTypeIndex для некоторого известного типа объекта и известково дельты

static volatile UCHAR guz; 

NTSTATUS getProcessIndex(USHORT& ObjectTypeIndex) 
{ 
    HANDLE hProcess; 
    NTSTATUS status = ZwDuplicateObject(NtCurrentProcess(), NtCurrentProcess(), NtCurrentProcess(), &hProcess, 0, 0, DUPLICATE_SAME_ACCESS); 

    if (0 <= status) 
    { 
     PVOID stack = alloca(guz); 
     DWORD cb = 0, rcb = 0x10000; 

     union { 
      PVOID buf; 
      PSYSTEM_HANDLE_INFORMATION_EX pshti; 
     }; 

     do 
     { 
      if (cb < rcb) cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack); 

      if (0 <= (status = ZwQuerySystemInformation(SystemExtendedHandleInformation, buf, cb, &rcb))) 
      { 
       if (ULONG NumberOfHandles = (ULONG)pshti->NumberOfHandles) 
       { 
        PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles = pshti->Handles; 

        ULONG_PTR UniqueProcessId = GetCurrentProcessId(); 
        do 
        { 
         if (Handles->UniqueProcessId == UniqueProcessId && (HANDLE)Handles->HandleValue == hProcess) 
         { 
          ObjectTypeIndex = Handles->ObjectTypeIndex; 
          goto __break; 
         } 

        } while (Handles++, --NumberOfHandles); 
       } 
      } 

     } while (STATUS_INFO_LENGTH_MISMATCH == status); 

__break: 
     ZwClose(hProcess); 
    } 

    return status; 
} 

class ZOBJECT_ALL_TYPES_INFORMATION 
{ 
    OBJECT_TYPE_INFORMATION* _TypeInformation; 
    DWORD _NumberOfTypes, _TypeIndexDelta; 

public: 

    operator DWORD() 
    { 
     return _NumberOfTypes; 
    } 

    operator OBJECT_TYPE_INFORMATION*() 
    { 
     return _TypeInformation; 
    } 

    DWORD operator[](OBJECT_TYPE_INFORMATION* TypeInformation) 
    { 
     return (DWORD)(TypeInformation - _TypeInformation) + _TypeIndexDelta; 
    } 

    OBJECT_TYPE_INFORMATION* operator[](DWORD Index) 
    { 
     return Index < _NumberOfTypes ? _TypeInformation + Index : 0; 
    } 

    ULONG TypeIndexToIndex(DWORD ObjectTypeIndex) 
    { 
     return ObjectTypeIndex -= _TypeIndexDelta; 
    } 

    OBJECT_TYPE_INFORMATION* operator[](PCUNICODE_STRING TypeName); 

    ZOBJECT_ALL_TYPES_INFORMATION(); 

    ~ZOBJECT_ALL_TYPES_INFORMATION(); 
}; 

ZOBJECT_ALL_TYPES_INFORMATION g_AOTI; 

OBJECT_TYPE_INFORMATION* ZOBJECT_ALL_TYPES_INFORMATION::operator[](PCUNICODE_STRING TypeName) 
{ 
    if (DWORD NumberOfTypes = _NumberOfTypes) 
    { 
     OBJECT_TYPE_INFORMATION* TypeInformation = _TypeInformation; 

     do 
     { 
      if (RtlEqualUnicodeString(TypeName, &TypeInformation->TypeName, TRUE)) 
      { 
       return TypeInformation; 
      } 
     } while (TypeInformation++, -- NumberOfTypes); 
    } 

    return 0; 
} 

ZOBJECT_ALL_TYPES_INFORMATION::ZOBJECT_ALL_TYPES_INFORMATION() 
{ 
    _TypeInformation = 0, _NumberOfTypes = 0; 

    USHORT ProcessTypeIndex; 
    if (0 > getProcessIndex(ProcessTypeIndex)) 
    { 
     return ; 
    } 

    NTSTATUS status; 
    PVOID stack = alloca(guz); 

    union { 
     PVOID pv; 
     OBJECT_TYPES_INFORMATION* poati; 
    }; 

    DWORD cb = 0, rcb = 0x2000; 
    do 
    { 
     if (cb < rcb) 
     { 
      cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack); 
     } 

     if (0 <= (status = ZwQueryObject(0, ObjectAllTypeInformation, poati, cb, &rcb))) 
     { 
      if (DWORD NumberOfTypes = poati->NumberOfTypes) 
      { 
       if (OBJECT_TYPE_INFORMATION* TypeInformation = (OBJECT_TYPE_INFORMATION*)LocalAlloc(0, rcb)) 
       { 
        _NumberOfTypes = NumberOfTypes; 
        _TypeInformation = TypeInformation; 

        ULONG Index = 0; 

        union { 
         ULONG_PTR uptr; 
         OBJECT_TYPE_INFORMATION* pti; 
        }; 

        union { 
         PWSTR buf; 
         PBYTE pb; 
         PVOID pv; 
        }; 

        pti = poati->TypeInformation; 
        pv = TypeInformation + NumberOfTypes; 

        do 
        { 
         STATIC_UNICODE_STRING_(Process); 

         if (RtlEqualUnicodeString(&Process, &pti->TypeName, TRUE)) 
         { 
          _TypeIndexDelta = ProcessTypeIndex - Index; 
         } 

         ULONG Length = pti->TypeName.Length, MaximumLength = pti->TypeName.MaximumLength; 
         memcpy(buf, pti->TypeName.Buffer, Length); 

         *TypeInformation = *pti; 
         TypeInformation++->TypeName.Buffer = buf; 
         pb += Length; 
         uptr += (sizeof(OBJECT_TYPE_INFORMATION) + MaximumLength + sizeof(PVOID)-1) & ~ (sizeof(PVOID)-1); 
        } while (Index++, --NumberOfTypes); 
       } 
      } 
     } 
    } while (status == STATUS_INFO_LENGTH_MISMATCH); 
} 

ZOBJECT_ALL_TYPES_INFORMATION::~ZOBJECT_ALL_TYPES_INFORMATION() 
{ 
    if (_TypeInformation) 
    { 
     LocalFree(_TypeInformation); 
    } 
} 

и, наконец, использовать следующий код, без NtQueryObject :

void TestMutant() 
{ 
    NTSTATUS status; 
    PVOID stack = alloca(guz); 
    DWORD cb = 0, rcb = 0x10000; 

    union { 
     PVOID buf; 
     PSYSTEM_HANDLE_INFORMATION_EX pshti; 
    }; 

    do 
    { 
     if (cb < rcb) cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack); 

     if (0 <= (status = ZwQuerySystemInformation(SystemExtendedHandleInformation, buf, cb, &rcb))) 
     { 
      if (ULONG NumberOfHandles = (ULONG)pshti->NumberOfHandles) 
      { 
       PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles = pshti->Handles; 

       ULONG_PTR UniqueProcessId = GetCurrentProcessId(); 
       do 
       { 
        if (Handles->UniqueProcessId == UniqueProcessId) 
        { 
         if (OBJECT_TYPE_INFORMATION* poti = g_AOTI[g_AOTI.TypeIndexToIndex(Handles->ObjectTypeIndex)]) 
         { 
          STATIC_UNICODE_STRING_(Mutant); 

          if (RtlEqualUnicodeString(&Mutant, &poti->TypeName, TRUE)) 
          { 
           MUTANT_BASIC_INFORMATION mbi; 
           QueryMutant((HANDLE)Handles->HandleValue, Handles->GrantedAccess, &mbi); 
          } 
         } 
        } 

       } while (Handles++, --NumberOfHandles); 
      } 
     } 

    } while (STATUS_INFO_LENGTH_MISMATCH == status); 
} 

можно проверить с

void Az() 
{ 
    HANDLE hMutant; 
    if (0 <= ZwCreateMutant(&hMutant, SYNCHRONIZE, 0, TRUE)) 
    { 
     TestMutant(); 
     ZwClose(hMutant); 
    } 
} 
+0

Я получаю ** 0x1F0001 ** для поля GrantedAccess, но, тем не менее, 'NtQueryMutant' терпит неудачу с тем же кодом состояния. – galenus

+0

@galenus - это странно, потому что достаточно #define MUTANT_QUERY_STATE 0x0001. Я тестирую сам себя и, например, смотрю https://github.com/mic101/windows/blob/master/WRK-v1.2/base/ntos/ex/mutant.c – RbMm

+0

@galenus нужно больше смотреть в живой системе, почему это случается. только на основе вашей информации трудно сказать – RbMm