Reputation: 2127
Disclaimer:
The only reason for the question and the code below to exist is an external component used in my application, which cannot be replaced, at least in the near future. This component's logic intercepts WinAPI calls from the application and performs various tasks based on these calls.
One of the things the component does, it creates mutex for each thread initialized inside the application. However, it doesn't close the mutexes, which results in handles leak.
Therefore, in order to prevent the leak and because I don't have access to the component's source code, I have to invent ugly workarounds and use esoteric API's.
End of disclaimer
I am trying to check state of mutexes in my application. In order to do this without changing the state of each of the objects I check, I have to use the NtQueryMutant
method from ntdll.dll.
Based on examples here and here I wrote the following code to achieve this:
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
}
The status
I receive here is always -1073741790 (0xFFFF FFFF C000 0022) which is, except being negative number, looks exactly like STATUS_ACCESS_DENIED.
That is very strange, because previously in code I use both NtQuerySystemInformation
and NtQueryObject
without any problem.
Additional details: my OS is Windows 7 SP1, the mutexes I try to query belong to the process I am performing the query from.
Upvotes: 0
Views: 939
Reputation: 33706
for effective test Mutant you need it handle and it access mask. you can got it from SYSTEM_HANDLE_INFORMATION_EX
structure. if we already have MUTANT_QUERY_STATE
- can direct query, if no - need reopen handle with 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;
}
and you not need all time use NtQueryObject for determinate type of handle. you can use SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX.ObjectTypeIndex
. for get OBJECT_TYPE_INFORMATION by this index. for this you need only once call ZwQueryObject(0, ObjectAllTypeInformation, )
at start, but exist problem how convert SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX.ObjectTypeIndex
to array index (zero bassed). begin from win8.1 'OBJECT_TYPE_INFORMATION.TypeIndex' is valid and match to SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX.ObjectTypeIndex
, but for early version - you need once get SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX.ObjectTypeIndex
for some known object type and calc delta
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);
}
}
and finally use next code, without 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);
}
can test with
void Az()
{
HANDLE hMutant;
if (0 <= ZwCreateMutant(&hMutant, SYNCHRONIZE, 0, TRUE))
{
TestMutant();
ZwClose(hMutant);
}
}
Upvotes: 2