Passing a byte array to WMI provider class in C++ - c++

I have an implementation of WMI client application in C++. I am looking to call ProtectKeyWithExternalKey method of the Win32_EncryptableVolume class. It takes a byte array as parameter (ExternalKey).
uint32 ProtectKeyWithExternalKey(
[in, optional] string FriendlyName,
[in, optional] uint8 ExternalKey[],
[out] string VolumeKeyProtectorID
);
When this method will be called using ExecMethod of IWbemServices class, we need to set IWbemClassObject object for input params and pass to ExecMethod.
virtual HRESULT STDMETHODCALLTYPE ExecMethod(
/* [in] */ __RPC__in const BSTR strObjectPath,
/* [in] */ __RPC__in const BSTR strMethodName,
/* [in] */ long lFlags,
/* [in] */ __RPC__in_opt IWbemContext *pCtx,
/* [in] */ __RPC__in_opt IWbemClassObject *pInParams,
/* [unique][in][out] */ __RPC__deref_opt_inout_opt IWbemClassObject **ppOutParams,
/* [unique][in][out] */ __RPC__deref_opt_inout_opt IWbemCallResult **ppCallResult) = 0;
While setting IWbemClassObject, VARIANT type needs to be set.
virtual HRESULT STDMETHODCALLTYPE Put(
/* [string][in] */ LPCWSTR wszName,
/* [in] */ long lFlags,
/* [in] */ VARIANT *pVal,
/* [in] */ CIMTYPE Type) = 0;
What should be set to VARIANT (VARTYPE and member of union) for byte array (uint8[])?
typedef /* [wire_marshal] */ struct tagVARIANT VARIANT;
struct tagVARIANT
{
union
{
struct __tagVARIANT
{
VARTYPE vt;
WORD wReserved1;
WORD wReserved2;
WORD wReserved3;
union
{
LONGLONG llVal;
LONG lVal;
BYTE bVal;
SHORT iVal;
FLOAT fltVal;
DOUBLE dblVal;
VARIANT_BOOL boolVal;
_VARIANT_BOOL bool;
SCODE scode;
CY cyVal;
DATE date;
BSTR bstrVal;
IUnknown *punkVal;
IDispatch *pdispVal;
SAFEARRAY *parray;
BYTE *pbVal;
SHORT *piVal;
LONG *plVal;
LONGLONG *pllVal;
FLOAT *pfltVal;
DOUBLE *pdblVal;
VARIANT_BOOL *pboolVal;
_VARIANT_BOOL *pbool;
SCODE *pscode;
CY *pcyVal;
DATE *pdate;
BSTR *pbstrVal;
IUnknown **ppunkVal;
IDispatch **ppdispVal;
SAFEARRAY **pparray;
VARIANT *pvarVal;
PVOID byref;
CHAR cVal;
USHORT uiVal;
ULONG ulVal;
ULONGLONG ullVal;
INT intVal;
UINT uintVal;
DECIMAL *pdecVal;
CHAR *pcVal;
USHORT *puiVal;
ULONG *pulVal;
ULONGLONG *pullVal;
INT *pintVal;
UINT *puintVal;
struct __tagBRECORD
{
PVOID pvRecord;
IRecordInfo *pRecInfo;
} __VARIANT_NAME_4;
} __VARIANT_NAME_3;
} __VARIANT_NAME_2;
DECIMAL decVal;
} __VARIANT_NAME_1;
} ;
Should it be BYTE* for union member? If yes, that would be pointer to unsigned char. How the size of byte array determined? Is it by null char for end of array? In other words, for passing byte array, is uint8* sufficient and size of array is not required?
And what should be initialized to VARTYPE member? Here is the enum.
enum VARENUM
{
VT_EMPTY = 0,
VT_NULL = 1,
VT_I2 = 2,
VT_I4 = 3,
VT_R4 = 4,
VT_R8 = 5,
VT_CY = 6,
VT_DATE = 7,
VT_BSTR = 8,
VT_DISPATCH = 9,
VT_ERROR = 10,
VT_BOOL = 11,
VT_VARIANT = 12,
VT_UNKNOWN = 13,
VT_DECIMAL = 14,
VT_I1 = 16,
VT_UI1 = 17,
VT_UI2 = 18,
VT_UI4 = 19,
VT_I8 = 20,
VT_UI8 = 21,
VT_INT = 22,
VT_UINT = 23,
VT_VOID = 24,
VT_HRESULT = 25,
VT_PTR = 26,
VT_SAFEARRAY = 27,
VT_CARRAY = 28,
VT_USERDEFINED = 29,
VT_LPSTR = 30,
VT_LPWSTR = 31,
VT_RECORD = 36,
VT_INT_PTR = 37,
VT_UINT_PTR = 38,
VT_FILETIME = 64,
VT_BLOB = 65,
VT_STREAM = 66,
VT_STORAGE = 67,
VT_STREAMED_OBJECT = 68,
VT_STORED_OBJECT = 69,
VT_BLOB_OBJECT = 70,
VT_CF = 71,
VT_CLSID = 72,
VT_VERSIONED_STREAM = 73,
VT_BSTR_BLOB = 0xfff,
VT_VECTOR = 0x1000,
VT_ARRAY = 0x2000,
VT_BYREF = 0x4000,
VT_RESERVED = 0x8000,
VT_ILLEGAL = 0xffff,
VT_ILLEGALMASKED = 0xfff,
VT_TYPEMASK = 0xfff
} ;
Should it be VT_BLOB?
VT_BLOB [P] Length prefixed bytes
Please help. Thanks in advance!

Related

COM - C++ Invoke function return DISP_E_TYPEMISMATCH [duplicate]

This question already has an answer here:
IDispatch Invoke() returns Type mismatch
(1 answer)
Closed 2 years ago.
I'm currently trying to call an ActiveX function of a software using COM.
here is the function declaration :
[
id(0xa),
helpcontext(0)
]
HRESULT MAIN_GetVersion (
[out] short* nStatus,
[out] BSTR* szStatusMsg,
[out] BSTR* szVersion
);
Here is how I do it :
short errorCode = 0;
BSTR errorMessageBSTR = SysAllocString(L"test");
BSTR versionBSTR = SysAllocString(L"test");
VARIANT result;
DISPPARAMS params = {NULL, NULL, 0, 0};
VARIANTARG args[3];
//First argument : Short*
VariantInit(&args[0]);
args[0].vt = VT_I2 | VT_BYREF;
args[0].piVal = &errorCode;
//Second argument : BSTR*
VariantInit(&args[1]);
args[1].vt = VT_BSTR | VT_BYREF;
args[1].pbstrVal = &errorMessageBSTR;
//Third argument : BSTR*
VariantInit(&args[2]);
args[2].vt = VT_BSTR | VT_BYREF;
args[2].pbstrVal = &versionBSTR;
params.rgvarg = args;
params.cArgs = 3;
DISPID dispID;
char szName[200];
// Convert down to ANSI
wchar functionName[] = L"MAIN_GetVersion";
wchar_t* functionNamePtr = functionName;
WideCharToMultiByte(CP_ACP, 0, functionName, -1, szName, 256, NULL, NULL);
CheckHResult(App.pdispVal->GetIDsOfNames(IID_NULL, &functionNamePtr, 1, LOCALE_USER_DEFAULT, &dispID) ,szName , dispID);
CheckHResult(App.pdispVal->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &params, &result, NULL, NULL), szName , dispID);
/*
return :
DISP_E_TYPEMISMATCH
One or more of the arguments could not be coerced. The index of the first parameter with the incorrect type within rgvarg is returned in puArgErr.
*/
For some reason, this code don't work and return DISP_E_TYPEMISMATCH. For some other reason, if I set all my VARIANT vartype'semphasized text to VT_EMPTY it work but I have nothing back from result VARIANT.
Is it possible the activeX implementation of the software I try to drive is wrong ?
Am I doing something wrong ?!
When building a DISPPARAMS to setup somes arg's, you need to sort them reverse order of their calls. In my case :
//First argument : Short*
VariantInit(&args[2]);
args[2].vt = VT_I2 | VT_BYREF;
args[2].piVal = &errorCode;
//Second argument : BSTR*
VariantInit(&args[1]);
args[1].vt = VT_BSTR | VT_BYREF;
args[1].pbstrVal = &errorMessageBSTR;
//Third argument : BSTR*
VariantInit(&args[0]);
args[0].vt = VT_BSTR | VT_BYREF;
args[0].pbstrVal = &versionBSTR;
The short is the last VARIANT of my VARIANT ARRAY.

Query metadata for NTFS special files as unprivileged user?

From an unprivileged user context, how can I query the size of the NTFS special files?
The size is the most important piece of metadata for me, but if I could get everything that is typically found in WIN32_FIND_DATA I would not mind it.
The NTFS special files I mean are (among others): $Mft, $MftMirr, $LogFile, $BadClus et cetera.
In order to open the MFT, I'd have to acquire certain privileges, open the volume and then parse the MFT. So that's out.
Also it seems to be impossible to open these files by name (for the majority of them), which precludes NtQueryInformationFile() and GetFileInformationByHandle(). Or maybe there is a combination of flags I haven't tried and it is possible to open them somehow for querying the file information?
Last but not least I don't get these files returned when using the respective Win32 APIs (FindFirstFile() et. al.), nor with NtQueryDirectoryFile() nor by using IRP_MN_QUERY_DIRECTORY directly.
Yes, I understand that I can effectively get the size of the MFT using FSCTL_GET_NTFS_VOLUME_DATA, but that's just one of these special files.
on ntfs volume we can enumerate all file records with FSCTL_GET_NTFS_FILE_RECORD. unfortunatelly format of FileRecordBuffer is undocumented/undeclared in windows headers. but this is common ntfs structs. buffer begin with NTFS_RECORD_HEADER (base class) after which will be several NTFS_ATTRIBUTE records. partial and custom definitions:
union NTFS_FILE_ID
{
LONGLONG IndexNumber;
struct
{
LONGLONG MftRecordIndex : 48;
LONGLONG SequenceNumber : 16;
};
};
struct NTFS_RECORD_HEADER
{
enum {
FILE = 'ELIF',
INDX = 'XDNI',
BAAD = 'DAAB',
HOLE = 'ELOH',
CHKD = 'DKHC'
} Type;
USHORT UsaOffset;
USHORT UsaCount;
USN Usn;
};
struct NTFS_FILE_RECORD_HEADER : public NTFS_RECORD_HEADER
{
USHORT SequenceNumber;
USHORT LinkCount;
USHORT AttributesOffset;
USHORT Flags;
ULONG BytesInUse;
ULONG BytesAllocated;
ULONGLONG BaseFileRecord;
USHORT NextAttributeNumber;
enum{
flgInUse = 1, flgDirectory = 2
};
};
struct NTFS_ATTRIBUTE
{
enum ATTRIBUTE_TYPE {
StandardInformation = 0x10,
AttributeList = 0x20,
FileName = 0x30,
ObjectId = 0x40,
SecurityDescriptor = 0x50,
VolumeName = 0x60,
VolumeInformation = 0x70,
Data = 0x80,
IndexRoot = 0x90,
IndexAllocation = 0xa0,
Bitmap = 0xb0,
ReparsePoint = 0xc0,
EAInformation = 0xd0,
EA = 0xe0,
PropertySet = 0xf0,
LoggedUtilityStream = 0x100,
StopTag = MAXDWORD
} Type;
ULONG Length;
BOOLEAN Nonresident;
UCHAR NameLength;
USHORT NameOffset;
USHORT Flags;// 1 = Compresed
USHORT AttributeNumber;
};
struct NTFS_RESIDENT_ATTRIBUTE : public NTFS_ATTRIBUTE
{
ULONG ValueLength;
USHORT ValueOffset;
USHORT Flags;
};
struct NTFS_NONRESIDENT_ATTRIBUTE : public NTFS_ATTRIBUTE
{
LONGLONG LowVcn;
LONGLONG HighVcn;
USHORT RunArrayOffset;
UCHAR CompressionUnit;
UCHAR Unknown[5];
LONGLONG AllocationSize;
LONGLONG DataSize;
LONGLONG InitializedSize;
LONGLONG CompressedSize;
};
struct NTFS_ATTRIBUTE_LIST
{
NTFS_ATTRIBUTE::ATTRIBUTE_TYPE Type;
USHORT Length;
UCHAR NameLength;
UCHAR NameOffset;
LONGLONG LowVcn;
LONGLONG FileReferenceNumber : 48;
LONGLONG FileReferenceNumber2 : 16;
USHORT AttributeNumber;
USHORT Unknown[3];
};
struct NTFS_STANDARD_ATTRIBUTE
{
LONGLONG CreationTime;
LONGLONG ChangeTime;
LONGLONG LastWriteTime;
LONGLONG LastAccessTime;
ULONG FileAttributes;
ULONG Unknown[3];
ULONG QuotaId;
ULONG SecurityId;
ULONGLONG QuotaChange;
USN Usn;
};
struct NTFS_FILENAME_ATTRIBUTE
{
NTFS_FILE_ID DirectoryId;
LONGLONG CreationTime;
LONGLONG ChangeTime;
LONGLONG LastWriteTime;
LONGLONG LastAccessTime;
LONGLONG AllocationSize;
LONGLONG DataSize;
ULONG FileAttributes;
ULONG EaSize;
UCHAR FileNameLength;// in symbols !!
UCHAR NameType;
WCHAR FileName[];
enum {
systemName , longName, shortName, systemName2
};
};
the code of enumeration all files can look like:
inline ULONG BOOL_TO_ERROR(BOOL f)
{
return f ? NOERROR : GetLastError();
}
ULONG QFMD(PCWSTR szVolumeName)
{
HANDLE hVolume = CreateFile(szVolumeName, FILE_GENERIC_READ, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (hVolume == INVALID_HANDLE_VALUE)
{
return GetLastError();
}
ULONG cb, BytesReturned;
NTFS_VOLUME_DATA_BUFFER nvdb;
ULONG err = BOOL_TO_ERROR(DeviceIoControl(hVolume, FSCTL_GET_NTFS_VOLUME_DATA, 0, 0, &nvdb, sizeof(nvdb), &BytesReturned, 0));
if (err == NOERROR)
{
NTFS_FILE_RECORD_INPUT_BUFFER nfrib;
cb = FIELD_OFFSET(NTFS_FILE_RECORD_OUTPUT_BUFFER, FileRecordBuffer[nvdb.BytesPerFileRecordSegment]);
PNTFS_FILE_RECORD_OUTPUT_BUFFER pnfrob = (PNTFS_FILE_RECORD_OUTPUT_BUFFER)alloca(cb);
// search for maximum valid FileReferenceNumber
LONG a = 0, b = MAXLONG, o;
do
{
nfrib.FileReferenceNumber.QuadPart = o = (a + b) >> 1;
err = BOOL_TO_ERROR(DeviceIoControl(hVolume, FSCTL_GET_NTFS_FILE_RECORD,
&nfrib, sizeof nfrib, pnfrob, cb, &BytesReturned, 0));
err ? b = o : a = o + 1;
} while(a < b);
nfrib.FileReferenceNumber.QuadPart--;
DbgPrint("MftRecordCount=%u\n", nfrib.FileReferenceNumber.LowPart);
union {
PVOID FileRecordBuffer;
PBYTE pb;
NTFS_RECORD_HEADER* pnrh;
NTFS_FILE_RECORD_HEADER* pnfrh;
NTFS_ATTRIBUTE* pna;
NTFS_RESIDENT_ATTRIBUTE* pnra;
NTFS_NONRESIDENT_ATTRIBUTE* pnaa;
};
NTFS_FILE_ID nfi;
UNICODE_STRING us = { sizeof (nfi), sizeof (nfi), (PWSTR)&nfi };
OBJECT_ATTRIBUTES oa = { sizeof(oa), hVolume, &us };
do
{
FileRecordBuffer = pnfrob->FileRecordBuffer;
if (err = BOOL_TO_ERROR(DeviceIoControl(hVolume, FSCTL_GET_NTFS_FILE_RECORD,
&nfrib, sizeof nfrib, pnfrob, cb, &BytesReturned, 0)))
{
break;
}
// are really file
if (
pnrh->Type != NTFS_RECORD_HEADER::FILE ||
!(pnfrh->Flags & NTFS_FILE_RECORD_HEADER::flgInUse) ||
pnfrh->BaseFileRecord
)
{
continue;
}
ULONG FileAttributes = INVALID_FILE_ATTRIBUTES;
ULONGLONG FileSize = 0;
nfi.MftRecordIndex = pnfrob->FileReferenceNumber.QuadPart;
nfi.SequenceNumber = pnfrh->SequenceNumber;
pb += pnfrh->AttributesOffset;
for( ; ; )
{
NTFS_FILENAME_ATTRIBUTE* pnfa;
NTFS_STANDARD_ATTRIBUTE* pnsa;
switch (pna->Type)
{
case NTFS_ATTRIBUTE::StopTag:
goto __end;
case NTFS_ATTRIBUTE::FileName:
pnfa = (NTFS_FILENAME_ATTRIBUTE*)RtlOffsetToPointer(pnra, pnra->ValueOffset);
if (pnfa->NameType == NTFS_FILENAME_ATTRIBUTE::longName)
{
//DbgPrint("<< %.*S\n", pnfa->FileNameLength, pnfa->FileName);
}
break;
case NTFS_ATTRIBUTE::StandardInformation:
pnsa = (NTFS_STANDARD_ATTRIBUTE*)RtlOffsetToPointer(pnra, pnra->ValueOffset);
FileAttributes = pnsa->FileAttributes;
break;
case NTFS_ATTRIBUTE::Data:
FileSize += pna->Nonresident ? pnaa->DataSize : pnra->ValueLength;
break;
}
pb += pna->Length;
}
__end:;
//HANDLE hFile;
//IO_STATUS_BLOCK iosb;
//NTSTATUS status = NtOpenFile(&hFile, FILE_READ_ATTRIBUTES, &oa, &iosb, FILE_SHARE_VALID_FLAGS,
// FILE_OPEN_REPARSE_POINT| FILE_OPEN_BY_FILE_ID | FILE_OPEN_FOR_BACKUP_INTENT);
//if (0 <= status)
//{
// NtClose(hFile);
//}
} while (0 <= (nfrib.FileReferenceNumber.QuadPart = pnfrob->FileReferenceNumber.QuadPart - 1));
}
CloseHandle(hVolume);
return err;
}
some NTFS System Files, but this list already old, exist more system files. if want concrete system file query need assign it number to NTFS_FILE_RECORD_INPUT_BUFFER. little changed code for query sys files only:
ULONG QFMD(PCWSTR szVolumeName)
{
HANDLE hVolume = CreateFile(szVolumeName, FILE_GENERIC_READ, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (hVolume == INVALID_HANDLE_VALUE)
{
return GetLastError();
}
ULONG cb, BytesReturned;
NTFS_VOLUME_DATA_BUFFER nvdb;
ULONG err = BOOL_TO_ERROR(DeviceIoControl(hVolume, FSCTL_GET_NTFS_VOLUME_DATA, 0, 0, &nvdb, sizeof(nvdb), &BytesReturned, 0));
if (err == NOERROR)
{
NTFS_FILE_RECORD_INPUT_BUFFER nfrib;
nfrib.FileReferenceNumber.QuadPart = 0x30;
cb = FIELD_OFFSET(NTFS_FILE_RECORD_OUTPUT_BUFFER, FileRecordBuffer[nvdb.BytesPerFileRecordSegment]);
PNTFS_FILE_RECORD_OUTPUT_BUFFER pnfrob = (PNTFS_FILE_RECORD_OUTPUT_BUFFER)alloca(cb);
union {
PVOID FileRecordBuffer;
PBYTE pb;
NTFS_RECORD_HEADER* pnrh;
NTFS_FILE_RECORD_HEADER* pnfrh;
NTFS_ATTRIBUTE* pna;
NTFS_RESIDENT_ATTRIBUTE* pnra;
NTFS_NONRESIDENT_ATTRIBUTE* pnaa;
};
NTFS_FILE_ID nfi;
UNICODE_STRING us = { sizeof (nfi), sizeof (nfi), (PWSTR)&nfi };
OBJECT_ATTRIBUTES oa = { sizeof(oa), hVolume, &us };
do
{
FileRecordBuffer = pnfrob->FileRecordBuffer;
if (err = BOOL_TO_ERROR(DeviceIoControl(hVolume, FSCTL_GET_NTFS_FILE_RECORD,
&nfrib, sizeof nfrib, pnfrob, cb, &BytesReturned, 0)))
{
break;
}
// are really file
if (
pnrh->Type != NTFS_RECORD_HEADER::FILE ||
!(pnfrh->Flags & NTFS_FILE_RECORD_HEADER::flgInUse) ||
pnfrh->BaseFileRecord
)
{
continue;
}
ULONG FileAttributes = INVALID_FILE_ATTRIBUTES;
ULONGLONG FileSize = 0;
PCWSTR ShortName = 0, LongName = 0, SystemName = 0;
UCHAR ShortNameLength = 0, LongNameLength = 0, SystemNameLength = 0;
nfi.MftRecordIndex = pnfrob->FileReferenceNumber.QuadPart;
nfi.SequenceNumber = pnfrh->SequenceNumber;
pb += pnfrh->AttributesOffset;
BOOL bSysFile = FALSE;
for( ; ; )
{
union {
NTFS_FILENAME_ATTRIBUTE* pnfa;
NTFS_STANDARD_ATTRIBUTE* pnsa;
};
switch (pna->Type)
{
case NTFS_ATTRIBUTE::StopTag:
goto __end;
case NTFS_ATTRIBUTE::FileName:
pnfa = (NTFS_FILENAME_ATTRIBUTE*)RtlOffsetToPointer(pnra, pnra->ValueOffset);
switch (pnfa->NameType)
{
case NTFS_FILENAME_ATTRIBUTE::systemName:
case NTFS_FILENAME_ATTRIBUTE::systemName2:
bSysFile = TRUE;
SystemName = pnfa->FileName, SystemNameLength = pnfa->FileNameLength;
break;
case NTFS_FILENAME_ATTRIBUTE::longName:
LongName = pnfa->FileName, LongNameLength = pnfa->FileNameLength;
break;
case NTFS_FILENAME_ATTRIBUTE::shortName:
ShortName = pnfa->FileName, ShortNameLength = pnfa->FileNameLength;
break;
}
break;
case NTFS_ATTRIBUTE::StandardInformation:
pnsa = (NTFS_STANDARD_ATTRIBUTE*)RtlOffsetToPointer(pnra, pnra->ValueOffset);
FileAttributes = pnsa->FileAttributes;
break;
case NTFS_ATTRIBUTE::Data:
FileSize += pna->Nonresident ? pnaa->DataSize : pnra->ValueLength;
break;
}
pb += pna->Length;
}
__end:;
if (bSysFile)
{
HANDLE hFile;
IO_STATUS_BLOCK iosb;
NTSTATUS status = NtOpenFile(&hFile, FILE_READ_ATTRIBUTES, &oa, &iosb, FILE_SHARE_VALID_FLAGS,
FILE_OPEN_REPARSE_POINT| FILE_OPEN_BY_FILE_ID | FILE_OPEN_FOR_BACKUP_INTENT);
if (0 <= status)
{
NtClose(hFile);
}
char sz[32];
StrFormatByteSize64A(FileSize, sz, RTL_NUMBER_OF(sz));
DbgPrint("%I64u: %08x %s [%x] %.*S\n", pnfrob->FileReferenceNumber.QuadPart,
FileAttributes, sz, status, SystemNameLength, SystemName);
}
} while (0 <= (nfrib.FileReferenceNumber.QuadPart = pnfrob->FileReferenceNumber.QuadPart - 1));
}
CloseHandle(hVolume);
return err;
}
with it i got next result:
38: 10000006 0 bytes [0] $Deleted
34: 00000020 10.0 MB [0] $TxfLogContainer00000000000000000002
33: 00000020 10.0 MB [0] $TxfLogContainer00000000000000000001
32: 00000020 64.0 KB [0] $TxfLog.blf
31: 00000026 1.00 MB [0] $Tops
30: 80000006 0 bytes [0] $Txf
29: 00000006 0 bytes [0] $TxfLog
28: 00000026 27.0 MB [0] $Repair
27: 00000006 0 bytes [0] $RmMetadata
26: 20000026 0 bytes [c0000034] $Reparse
25: 20000026 0 bytes [c0000034] $ObjId
24: 20000026 0 bytes [c0000034] $Quota
11: 00000006 0 bytes [0] $Extend
10: 00000006 128 KB [0] $UpCase
9: 20000006 0 bytes [c0000034] $Secure
8: 00000006 237 GB [c0000022] $BadClus
7: 00000006 8.00 KB [c0000022] $Boot
6: 00000006 7.42 MB [c0000022] $Bitmap
5: 00000806 0 bytes [0] .
4: 00000006 2.50 KB [0] $AttrDef
3: 00000006 0 bytes [0] $Volume
2: 00000006 64.0 MB [c0000022] $LogFile
1: 00000006 4.00 KB [0] $MFTMirr
0: 00000006 212 MB [0] $MFT
Yes, it is possible to use DeviceIoControl / FSCTL_GET_NTFS_FILE_RECORD to read the $MFT without elevation. With the help of this page, I have worked out the minimum settings. Note the last few paragraphs at the bottom of that page.
in Group Policy gpedit.msc, add the (non-elevated) user account you'll be running under to the following to the following policies:
Windows Settings/Security Settings/Local Policies/User Rights Assignment/...Perform Volume Maintenance tasks (definitely needed)
Windows Settings/Security Settings/Local Policies/User Rights Assignment/...Back up files and directories (not sure if this is essential)
So far, I haven't needed the following, but make a note of it in case you need to come back to it:
Windows Settings/Security Settings/Local Policies/User Rights Assignment/...Restore files and directories
Run gpupdate.exe from a Windows command prompt, or wait about 15 minutes for the group policy changes to take effect.
Those changes allow your user account to acquire the privileges. As a one-time step every time your app starts, you'll have to explicitly adjust your token. Here's a standalone version of the Win32 API AdjustTokenPrivileges:
using System;
using System.Runtime.InteropServices;
using System.Security;
using System.ComponentModel;
[SuppressUnmanagedCodeSecurity]
public static class AdjPriv
{
[DllImport("kernel32.dll")]
static extern IntPtr GetCurrentProcess();
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool OpenProcessToken(IntPtr h, int acc, out IntPtr phtok);
[DllImport("advapi32.dll", SetLastError = true, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool LookupPrivilegeValueW(IntPtr host, [MarshalAs(UnmanagedType.LPWStr)] String name, out long pluid);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, in TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
const int
SE_PRIVILEGE_ENABLED /**/ = 0x00000002,
TOKEN_QUERY /**/ = 0x00000008,
TOKEN_ADJUST_PRIVILEGES /**/ = 0x00000020,
ERROR_NOT_ALL_ASSIGNED /**/ = 0x00000514;
[StructLayout(LayoutKind.Sequential, Pack = 4)]
struct TokPriv1Luid
{
public int Count;
public long Luid;
public int Attr;
};
public static bool SetPrivilege(String szSe)
{
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, out IntPtr htok))
goto _error;
var tp = new TokPriv1Luid { Count = 1, Attr = SE_PRIVILEGE_ENABLED };
if (!LookupPrivilegeValueW(IntPtr.Zero, szSe, out tp.Luid))
goto _error;
if (!AdjustTokenPrivileges(htok, false, in tp, 0, IntPtr.Zero, IntPtr.Zero))
goto _error;
return Marshal.GetLastWin32Error() != ERROR_NOT_ALL_ASSIGNED;
_error:
throw new Win32Exception();
}
};
Give the current user account the "SeManageVolumePrivilege" privilege when the app starts up by calling the AdjPriv.SetPrivilege utility function shown above. Call it once for each additional privilege you may also want to add.
static MyProgram()
{
if (!AdjPriv.SetPrivilege("SeManageVolumePrivilege"))
throw new SecurityException();
/// etc...
}
And now for the code. I won't go into detail with the p/Invoke since everyone has their own way of doing it. I'll just show the exact flags and constants values passed into the two critical two API calls in order to work without prompting for elevation.
IntPtr h = CreateFileW(#"\\?\Volume{c2655473-adc2-4fe3-99a0-77d5bb1b809f}\",
FILE_ACCESS_READ_CONTROL, // 0x00020000
FILE_SHARE_ANY, // 7
IntPtr.Zero,
CREATE_MODE_OPEN_EXISTING, // 3
FILE_FLAG_BACKUP_SEMANTICS, // 0x02000000
IntPtr.Zero);
And then finally...
/// <summary>
/// Given a 48-bit MFT index 'frn', recover the current "sequence number" of the file, which
/// can be used as the upper 16-bits to complete a usable FILE_REFERENCE (NTFS FileId):
/// </summary>
var frn = (FILE_REFERENCE)0x_0000_000000000218;
// ^^^^----- ???
// lookup proceeds downwards, so set the sought-after upper 16 bits to max. seq. value
frn.Seq = 0xFFFF;
if (!DeviceIoControl(h, FSCTL_GET_NTFS_FILE_RECORD, in frn, out FILE_RECORD_OUTPUT_BUFFER rec) ||
frn.Index != rec.file_ref.Index)
throw new Win32Exception();
frn.Seq = rec.frh.SequenceNumber;
// vvvv--- !!!
Console.WriteLine($"0x{frn:X16}"); // 0x_0092_000000000218
Notes:
It works! Well at least for me on Windows 10, Version 10.0.18362.387. Please notice the backslash ...\ at the end of the Volume Guid path in CreateFileW. The code will not work without it.

IntelliSense: a value of type "PVOID" cannot be used to initialize an entity of type "_NtQuerySystemInformation"

I want know all handles of files that is opened by a determinated process and i have this following code that was found here, but when is passed to VS comes a error saying:
IntelliSense: a value of type "PVOID" cannot be used to initialize an entity of type "_NtQuerySystemInformation"
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <stdio.h>
#define NT_SUCCESS(x) ((x) >= 0)
#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004
#define SystemHandleInformation 16
#define ObjectBasicInformation 0
#define ObjectNameInformation 1
#define ObjectTypeInformation 2
typedef NTSTATUS (NTAPI *_NtQuerySystemInformation)(
ULONG SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);
typedef NTSTATUS (NTAPI *_NtDuplicateObject)(
HANDLE SourceProcessHandle,
HANDLE SourceHandle,
HANDLE TargetProcessHandle,
PHANDLE TargetHandle,
ACCESS_MASK DesiredAccess,
ULONG Attributes,
ULONG Options
);
typedef NTSTATUS (NTAPI *_NtQueryObject)(
HANDLE ObjectHandle,
ULONG ObjectInformationClass,
PVOID ObjectInformation,
ULONG ObjectInformationLength,
PULONG ReturnLength
);
typedef struct _UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef struct _SYSTEM_HANDLE
{
ULONG ProcessId;
BYTE ObjectTypeNumber;
BYTE Flags;
USHORT Handle;
PVOID Object;
ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE, *PSYSTEM_HANDLE;
typedef struct _SYSTEM_HANDLE_INFORMATION
{
ULONG HandleCount;
SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
typedef enum _POOL_TYPE
{
NonPagedPool,
PagedPool,
NonPagedPoolMustSucceed,
DontUseThisType,
NonPagedPoolCacheAligned,
PagedPoolCacheAligned,
NonPagedPoolCacheAlignedMustS
} POOL_TYPE, *PPOOL_TYPE;
typedef struct _OBJECT_TYPE_INFORMATION
{
UNICODE_STRING Name;
ULONG TotalNumberOfObjects;
ULONG TotalNumberOfHandles;
ULONG TotalPagedPoolUsage;
ULONG TotalNonPagedPoolUsage;
ULONG TotalNamePoolUsage;
ULONG TotalHandleTableUsage;
ULONG HighWaterNumberOfObjects;
ULONG HighWaterNumberOfHandles;
ULONG HighWaterPagedPoolUsage;
ULONG HighWaterNonPagedPoolUsage;
ULONG HighWaterNamePoolUsage;
ULONG HighWaterHandleTableUsage;
ULONG InvalidAttributes;
GENERIC_MAPPING GenericMapping;
ULONG ValidAccess;
BOOLEAN SecurityRequired;
BOOLEAN MaintainHandleCount;
USHORT MaintainTypeList;
POOL_TYPE PoolType;
ULONG PagedPoolUsage;
ULONG NonPagedPoolUsage;
} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
PVOID GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName)
{
return GetProcAddress(GetModuleHandleA(LibraryName), ProcName);
}
int wmain(int argc, WCHAR *argv[])
{
_NtQuerySystemInformation NtQuerySystemInformation =
GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation");
_NtDuplicateObject NtDuplicateObject =
GetLibraryProcAddress("ntdll.dll", "NtDuplicateObject");
_NtQueryObject NtQueryObject =
GetLibraryProcAddress("ntdll.dll", "NtQueryObject");
NTSTATUS status;
PSYSTEM_HANDLE_INFORMATION handleInfo;
ULONG handleInfoSize = 0x10000;
ULONG pid;
HANDLE processHandle;
ULONG i;
if (argc < 2)
{
printf("Usage: handles [pid]\n");
return 1;
}
pid = _wtoi(argv[1]);
if (!(processHandle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid)))
{
printf("Could not open PID %d! (Don't try to open a system process.)\n", pid);
return 1;
}
handleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(handleInfoSize);
/* NtQuerySystemInformation won't give us the correct buffer size,
so we guess by doubling the buffer size. */
while ((status = NtQuerySystemInformation(
SystemHandleInformation,
handleInfo,
handleInfoSize,
NULL
)) == STATUS_INFO_LENGTH_MISMATCH)
handleInfo = (PSYSTEM_HANDLE_INFORMATION)realloc(handleInfo, handleInfoSize *= 2);
/* NtQuerySystemInformation stopped giving us STATUS_INFO_LENGTH_MISMATCH. */
if (!NT_SUCCESS(status))
{
printf("NtQuerySystemInformation failed!\n");
return 1;
}
for (i = 0; i < handleInfo->HandleCount; i++)
{
SYSTEM_HANDLE handle = handleInfo->Handles[i];
HANDLE dupHandle = NULL;
POBJECT_TYPE_INFORMATION objectTypeInfo;
PVOID objectNameInfo;
UNICODE_STRING objectName;
ULONG returnLength;
/* Check if this handle belongs to the PID the user specified. */
if (handle.ProcessId != pid)
continue;
/* Duplicate the handle so we can query it. */
if (!NT_SUCCESS(NtDuplicateObject(
processHandle,
handle.Handle,
GetCurrentProcess(),
&dupHandle,
0,
0,
0
)))
{
printf("[%#x] Error!\n", handle.Handle);
continue;
}
/* Query the object type. */
objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000);
if (!NT_SUCCESS(NtQueryObject(
dupHandle,
ObjectTypeInformation,
objectTypeInfo,
0x1000,
NULL
)))
{
printf("[%#x] Error!\n", handle.Handle);
CloseHandle(dupHandle);
continue;
}
/* Query the object name (unless it has an access of
0x0012019f, on which NtQueryObject could hang. */
if (handle.GrantedAccess == 0x0012019f)
{
/* We have the type, so display that. */
printf(
"[%#x] %.*S: (did not get name)\n",
handle.Handle,
objectTypeInfo->Name.Length / 2,
objectTypeInfo->Name.Buffer
);
free(objectTypeInfo);
CloseHandle(dupHandle);
continue;
}
objectNameInfo = malloc(0x1000);
if (!NT_SUCCESS(NtQueryObject(
dupHandle,
ObjectNameInformation,
objectNameInfo,
0x1000,
&returnLength
)))
{
/* Reallocate the buffer and try again. */
objectNameInfo = realloc(objectNameInfo, returnLength);
if (!NT_SUCCESS(NtQueryObject(
dupHandle,
ObjectNameInformation,
objectNameInfo,
returnLength,
NULL
)))
{
/* We have the type name, so just display that. */
printf(
"[%#x] %.*S: (could not get name)\n",
handle.Handle,
objectTypeInfo->Name.Length / 2,
objectTypeInfo->Name.Buffer
);
free(objectTypeInfo);
free(objectNameInfo);
CloseHandle(dupHandle);
continue;
}
}
/* Cast our buffer into an UNICODE_STRING. */
objectName = *(PUNICODE_STRING)objectNameInfo;
/* Print the information! */
if (objectName.Length)
{
/* The object has a name. */
printf(
"[%#x] %.*S: %.*S\n",
handle.Handle,
objectTypeInfo->Name.Length / 2,
objectTypeInfo->Name.Buffer,
objectName.Length / 2,
objectName.Buffer
);
}
else
{
/* Print something else. */
printf(
"[%#x] %.*S: (unnamed)\n",
handle.Handle,
objectTypeInfo->Name.Length / 2,
objectTypeInfo->Name.Buffer
);
}
free(objectTypeInfo);
free(objectNameInfo);
CloseHandle(dupHandle);
}
free(handleInfo);
CloseHandle(processHandle);
return 0;
}
How solve this problem?
Thank in advance.
You need to cast pointer to void to pointer to function. Note that in general case size of pointer to function and size of pointer to data may be not the same.
_NtQuerySystemInformation NtQuerySystemInformation{};
{
auto const p_proc{GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation")};
assert(p_proc); // TODO handle error...
static_assert(sizeof(_NtQuerySystemInformation) == sizeof(p_proc), "pointer size mismatch");
memcpy(&NtQuerySystemInformation, &p_proc, sizeof(NtQuerySystemInformation)); // deep copy
}
in case of C (because code in your question actually seems to be written in C):
_NtQuerySystemInformation NtQuerySystemInformation = NULL;
{
PVOID const p_proc = GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation");
assert(p_proc); // TODO handle error...
assert(sizeof(_NtQuerySystemInformation) == sizeof(p_proc));
memcpy(&NtQuerySystemInformation, &p_proc, sizeof(NtQuerySystemInformation)); // deep copy
}

Memory leak calling .Net dll from C++

I'm getting a memory leak when calling a .Net dll from C++ in the function below. I'm thinking it is with the SafeArray declaration but I don't know how else to release the memory besides SafeArrayDestroyDescriptor. What else am I missing?
VARIANT_BOOL SendPack(IDotNetDll* wb, WW_POLL pl)
{
HRESULT hr = 0;
VARIANT_BOOL bretval;
BYTE destination = pl.uDest;
BYTE raw[2];
raw[0] = pl.uAPI;
raw[1] = pl.uOpcode;
SAFEARRAY* bytes = NULL;
hr = SafeArrayAllocDescriptor(1, &bytes);
bytes->cbElements = sizeof(raw[0]);
bytes->rgsabound[0].cElements = sizeof(raw);
bytes->rgsabound[0].lLbound = 0;
bytes->pvData = raw;
bytes->fFeatures = FADF_AUTO | FADF_FIXEDSIZE;
wb->SendMessage(destination,(BYTE)4, bytes, VARIANT_FALSE,
200.0,&bretval);
SafeArrayDestroyDescriptor(bytes);
return bretval;
}
Edit:
I also tried this method using a variant
VARIANT_BOOL SendPacket(IDotNetDll* wb, WW_POLL pl)
{
HRESULT hr = 0;
VARIANT_BOOL bretval;
_variant_t var;
void * pArrayData = NULL;
var.vt = VT_ARRAY | VT_UI1;
SAFEARRAYBOUND rgsabound1[1];
rgsabound1[0].cElements = 5;
rgsabound1[0].lLbound = 0;
var.parray = SafeArrayCreate(VT_UI1, 1, rgsabound1);
BYTE destination = pl.uDest;
SafeArrayAccessData(var.parray, &pArrayData);
BYTE raw[5];
raw[0] = pl.uAPI;
raw[1] = pl.uOpcode;
raw[2] = pl.uPayLoad[0];
raw[3] = pl.uPayLoad[1];
raw[4] = pl.uPayLoad[2];
memcpy(pArrayData, raw, 5);
SafeArrayUnaccessData(var.parray);
//Send packet
wb->SendMessage(destination,
(BYTE)4,
var.parray,
VARIANT_FALSE,
200.0, &bretval);
var.Clear();
return bretval;
}
Here is the memory usage from VS 2015 profiler

Does std::lock_guard increment the dll reference counter? (LoadCount)

I have come across a weird thing while using the std::lock_guard.
NOTE: I am using MSVS 2012 and the std::lock_guard as declared in the mutex header from the new c++11 headers that are included and shipped in this version.
The following is code to replicate this behaviour, and it seems like using this function increments the dll LoadCount for some reason, my question is if anyone can explain why, and is there a better lock function I can use that wont mess with the LoadCount?
#include <windows.h>
#include <mutex>
typedef struct _PEB_LDR_DATA {
ULONG Length;
BOOLEAN Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
typedef struct _LSA_UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING;
typedef LSA_UNICODE_STRING UNICODE_STRING, *PUNICODE_STRING;
typedef struct _RTL_USER_PROCESS_PARAMETERS {
BYTE Reserved1[16];
PVOID Reserved2[10];
UNICODE_STRING ImagePathName;
UNICODE_STRING CommandLine;
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
typedef VOID (NTAPI *PPS_POST_PROCESS_INIT_ROUTINE) (VOID);
typedef struct _PEB {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
BYTE Reserved4[104];
PVOID Reserved5[52];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
BYTE Reserved6[128];
PVOID Reserved7[1];
ULONG SessionId;
} PEB, *PPEB;
typedef struct _PROCESS_BASIC_INFORMATION
{
PVOID Reserved1;
PPEB PebBaseAddress;
PVOID Reserved2[2];
ULONG_PTR UniqueProcessId;
PVOID Reserved3;
} PROCESS_BASIC_INFORMATION;
typedef enum _PROCESSINFOCLASS
{
ProcessBasicInformation = 0,
} PROCESSINFOCLASS, *PPROCESSINFOCLASS;
typedef struct _LDRP_CSLIST
{
PSINGLE_LIST_ENTRY Tail;
} LDRP_CSLIST, *PLDRP_CSLIST;
typedef enum _LDR_DDAG_STATE
{
LdrModulesMerged = -5,
LdrModulesInitError = -4,
LdrModulesSnapError = -3,
LdrModulesUnloaded = -2,
LdrModulesUnloading = -1,
LdrModulesPlaceHolder = 0,
LdrModulesMapping = 1,
LdrModulesMapped = 2,
LdrModulesWaitingForDependencies = 3,
LdrModulesSnapping = 4,
LdrModulesSnapped = 5,
LdrModulesCondensed = 6,
LdrModulesReadyToInit = 7,
LdrModulesInitializing = 8,
LdrModulesReadyToRun = 9
} LDR_DDAG_STATE;
typedef struct _LDR_DDAG_NODE
{
LIST_ENTRY Modules;
PVOID ServiceTagList;
ULONG LoadCount;//this is where its located in windows 8
ULONG ReferenceCount;
ULONG DependencyCount;
union
{
LDRP_CSLIST Dependencies;
SINGLE_LIST_ENTRY RemovalLink;
};
LDRP_CSLIST IncomingDependencies;
LDR_DDAG_STATE State;
SINGLE_LIST_ENTRY CondenseLink;
ULONG PreorderNumber;
ULONG LowestLink;
} LDR_DDAG_NODE, *PLDR_DDAG_NODE;
typedef struct _LDR_MODULE {
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID BaseAddress;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT obsoleteLoadCount;//in windows 8 this is obsolete
USHORT TlsIndex;//but we can still use it in win 7 and below
union
{
LIST_ENTRY HashLinks;
struct CheckPtr
{
PVOID SectionPointer;
ULONG CheckSum;
};
};
union
{
ULONG TimeDateStamp;
PVOID LoadedImports;
};
struct _ACTIVATION_CONTEXT *EntryPointActivationContext;
PVOID PatchInformation;
PLDR_DDAG_NODE DdagNode;
} LDR_MODULE, *PLDR_MODULE;
typedef NTSTATUS (__stdcall *pfnZwQueryInformationProcess) (HANDLE, PROCESSINFOCLASS,
PVOID, ULONG, PULONG);
pfnZwQueryInformationProcess ZwQueryInformationProcess;
DWORD GetModuleLoadCount(HMODULE hmod)
{
HMODULE hModule = LoadLibrary("ntdll.dll");
if(hModule==NULL)
return NULL;
ZwQueryInformationProcess = (pfnZwQueryInformationProcess) GetProcAddress(hModule,
"ZwQueryInformationProcess");
if (ZwQueryInformationProcess == NULL) {
FreeLibrary(hModule)
return NULL; // failed to get PEB
}
PROCESS_BASIC_INFORMATION pbi;
PROCESSINFOCLASS pic = ProcessBasicInformation;
if (ZwQueryInformationProcess(GetCurrentProcess(), pic, &pbi, sizeof(pbi), NULL)
!= STATUS_SUCCESS)
{
// ZwQueryInformationProcess failed...
FreeLibrary(hModule);
return NULL;
}
FreeLibrary(hModule);
LDR_MODULE *peb_ldr_module = (LDR_MODULE
*)pbi.PebBaseAddress->Ldr->InLoadOrderModuleList.Flink;
while((peb_ldr_module = (LDR_MODULE
*)peb_ldr_module->InLoadOrderModuleList.Flink)!=(LDR_MODULE
*)pbi.PebBaseAddress->Ldr->InLoadOrderModuleList.Blink) {
if(peb_ldr_module->BaseAddress==hmod) {
//well this actualy works in windows 8...
//and probably vista with aero enabled as well...
//anyway if it is obsolete its always 6
//so we can if it out like this...
if(peb_ldr_module->obsoleteLoadCount==6)
return peb_ldr_module->DdagNode->LoadCount;
else
return peb_ldr_module->obsoleteLoadCount;
}
}
if(peb_ldr_module->BaseAddress==hmod) {
if(peb_ldr_module->obsoleteLoadCount==6)
return peb_ldr_module->DdagNode->LoadCount;
else
return peb_ldr_module->obsoleteLoadCount;
}
}
mutable std::mutex g_logMutex;
void test()//test is just a function inside of my dll
{ //which is injected and running under low privileges in internet explorer as
//an addon, I dont think that matters, but i dont want to leave any info out
//that might help answer my question.
int loadcount = GetModuleLoadCount((HMODULE)gModule);//its 1 here
std::lock_guard<std::mutex> lock(g_logMutex);
loadcount = GetModuleLoadCount((HMODULE)gModule);//now its 2
}
If it does, it's undefined behaviour. C++ doesn't "know" about DLLs (since DLLs are Windows-specific).
More likely, you're either seeing a race-condition in action, or the std::lock_guard implementation you're using is leaking a module handle.