program crashes on calling RmShutdown() - c

I'm experiencing a strange issue with the Restart Manager API.
I'm trying to debug a runtime error that is causing my program to crash in the middle of execution.
I have created a logging system that writes a simple log text file during the course of execution. I tried to debug the program, but it doesn't appear in my system, and the program crashes on calling RmShutdown().
This function crashes on specific files, and sometimes it doesn't appear. For example, most times it appears on C:\Users\Administrator\ntuser.dat.LOG1.
I know I shouldn't kill a system process, but it's a test when the crash happens. Does killing a process that has a handle of file make my program crash?
What could possibly be wrong here? Why would RmShutdown() fail?
BOOL KillFileOwner(__in LPCWSTR PathName)
{
BOOL Result = FALSE;
DWORD dwSession = 0xFFFFFFFF; // Invalid handle value
DWORD ret = 0;
WCHAR szSessionKey[CCH_RM_SESSION_KEY + 1] = { 0 };
if (RmStartSession(&dwSession, 0, szSessionKey) == ERROR_SUCCESS)
{
if (RmRegisterResources(dwSession, 1, &PathName, 0, NULL, 0, NULL) == ERROR_SUCCESS)
{
DWORD dwReason = 0x0;
UINT nProcInfoNeeded = 0;
UINT nProcInfo = 0;
PRM_PROCESS_INFO ProcessInfo = NULL;
// RtlSecureZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
ret = (DWORD)RmGetList(dwSession, &nProcInfoNeeded, &nProcInfo, NULL, &dwReason);
if (ret != ERROR_MORE_DATA || !nProcInfoNeeded) {
RmEndSession(dwSession);
return FALSE;
}
ProcessInfo = (PRM_PROCESS_INFO)calloc(nProcInfoNeeded, sizeof(RM_PROCESS_INFO));
if (!ProcessInfo) {
RmEndSession(dwSession);
return FALSE;
}
nProcInfo = nProcInfoNeeded;
ret = (DWORD)RmGetList(dwSession, &nProcInfoNeeded, &nProcInfo, ProcessInfo, &dwReason);
if (ret != ERROR_SUCCESS || !nProcInfoNeeded) {
free(ProcessInfo);
RmEndSession(dwSession);
return FALSE;
}
DWORD ProcessId = (DWORD)GetProcessId(GetCurrentProcess());
if (!ProcessId) {
free(ProcessInfo);
RmEndSession(dwSession);
return FALSE;
}
for (UINT i = 0; i < nProcInfo; i++) {
if (ProcessInfo[i].Process.dwProcessId == ProcessId) {
free(ProcessInfo);
RmEndSession(dwSession);
return FALSE;
}
}
Result = (RmShutdown(dwSession, RmForceShutdown, NULL) == ERROR_SUCCESS);
free(ProcessInfo);
}
RmEndSession(dwSession);
}
return Result;
}

Related

Defragmenting File Programatically On Windows

I am attempting to follow this tutorial: Defragmenting Files.
I call DeviceIoControl() with FSCTL_GET_VOLUME_BITMAP on a handle to the C: volume, and I get a proper response.
I then open a handle to another file (I tried files from 10KB to a few MB) successfully, then I call DeviceIoControl() with FSCTL_GET_RETRIEVAL_POINTERS, and it succeeds with no last error or failed result, but the RETRIEVAL_POINTERS_BUFFER is not filled.
I also tried calling it on the C: volume handle, but it keeps returning ERROR_HANDLE_EOF even after trying to set the OVERLAPPED offset to 0, and setting the file pointer with SetFilePointer() to 0 relative to the beginning of the file.
BOOL dic(HANDLE dev, DWORD code, LPVOID in, DWORD ins, LPVOID out, LPDWORD outs)
{
HANDLE h = GetProcessHeap();
DWORD s = 1000;
DWORD r = 0;
out = HeapAlloc(h,HEAP_ZERO_MEMORY,s);
while (!DeviceIoControl(dev, code, in, ins, out, s, &r, 0))
{
if (ERROR_INSUFFICIENT_BUFFER == GetLastError() || ERROR_MORE_DATA == GetLastError())
{
s *= 10;
LPVOID t = HeapReAlloc(h, HEAP_ZERO_MEMORY, out, s);
if(!t){
HeapFree(h, 0, out);
return 0;
}
out = t;
}
else
{
HeapFree(h, 0, out);
printf("dic unk: %d\n", GetLastError());
return 0;
}
}
*outs = s;
return 1;
}
BOOL getvolptr(HANDLE volh, PRETRIEVAL_POINTERS_BUFFER rpb, LPDWORD rpbs)
{
STARTING_VCN_INPUT_BUFFER vcn = { 0 };
return dic(volh, FSCTL_GET_RETRIEVAL_POINTERS, &vcn, sizeof(vcn), rpb, rpbs);
}
RETRIEVAL_POINTERS_BUFFER rpb = { 0 };
DWORD rpbs = 0;
ULONGLONG cluster_cnt=0;
HANDLE fi = openfile("C:\\Windows\\System32\\Kernel32.dll");
if (INVALID_HANDLE_VALUE == fi)
{
printf("failed to open file! (%d)\n", GetLastError());
getchar();
}
r = getvolptr(fi, &rpb, &rpbs);
if (!r)
{
printf("failed to get vol ptrs! (%d)\n", GetLastError());
getchar();
}
for (int i = 0; i < rpb.ExtentCount; ++i)
{
cluster_cnt = (ULONGLONG)(rpb.Extents[i].NextVcn.QuadPart) - (ULONGLONG)(rpb.StartingVcn.QuadPart);
printf("%d) size: %llu clusters (0x%016X)\n", i, cluster_cnt, rpb.Extents[i].Lcn.QuadPart);
}
You are not checking the first HeapAlloc() for failure. And HeapFree() can wipe the last error code from DeviceIoControl() before you print it.
But more importantly, you are not passing the out data back to the caller correctly, so you are leaking the allocated memory, and the caller ends up with garbage for output.
Since the caller is passing in their own RETRIEVAL_POINTERS_BUFFER to receive the data, you need to copy the contents of the allocate memory into that buffer, eg:
BOOL dic(HANDLE dev, DWORD code, LPVOID in, DWORD ins, LPVOID out, LPDWORD outs)
{
if (!in || !out || !outs)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
*outs = 0;
HANDLE h = GetProcessHeap();
DWORD s = 1000;
LPVOID buf = HeapAlloc(h, HEAP_ZERO_MEMORY, s);
if (!buf)
return FALSE;
DWORD r = 0;
while (!DeviceIoControl(dev, code, in, ins, buf, s, &r, 0))
{
if (ERROR_INSUFFICIENT_BUFFER == GetLastError() || ERROR_MORE_DATA == GetLastError())
{
s *= 10;
LPVOID t = HeapReAlloc(h, HEAP_ZERO_MEMORY, buf, s);
if (!t)
{
HeapFree(h, 0, buf);
return FALSE;
}
buf = t;
}
else
{
printf("dic unk: %u\n", GetLastError());
HeapFree(h, 0, buf);
return FALSE;
}
}
if (s > *outs)
{
HeapFree(h, 0, buf);
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
CopyMemory(out, buf, s);
*outs = s;
HeapFree(h, 0, buf);
return TRUE;
}
BOOL getvolptr(HANDLE volh, PRETRIEVAL_POINTERS_BUFFER rpb, LPDWORD rpbs)
{
STARTING_VCN_INPUT_BUFFER vcn = { 0 };
return dic(volh, FSCTL_GET_RETRIEVAL_POINTERS, &vcn, sizeof(vcn), rpb, rpbs);
}
HANDLE fi = openfile("C:\\Windows\\System32\\Kernel32.dll");
if (INVALID_HANDLE_VALUE == fi)
{
printf("failed to open file! (%u)\n", GetLastError());
getchar();
}
else
{
RETRIEVAL_POINTERS_BUFFER rpb = { 0 };
DWORD rpbs = sizeof(rpb);
if (!getvolptr(fi, &rpb, &rpbs))
{
printf("failed to get vol ptrs! (%u)\n", GetLastError());
getchar();
}
else
{
ULONGLONG cluster_cnt = 0;
for (int i = 0; i < rpb.ExtentCount; ++i)
{
cluster_cnt = (ULONGLONG)(rpb.Extents[i].NextVcn.QuadPart) - (ULONGLONG)(rpb.StartingVcn.QuadPart);
printf("%d) size: %llu clusters (0x%016X)\n", i, cluster_cnt, rpb.Extents[i].Lcn.QuadPart);
}
}
closefile(fi);
}
Alternatively, you can return the pointer to the allocated memory to the caller, and the caller will have to free the memory when done using it, eg:
BOOL dic(HANDLE dev, DWORD code, LPVOID in, DWORD ins, LPVOID* out, LPDWORD outs)
{
if (!in || !out || !outs)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
*out = NULL;
*outs = 0;
HANDLE h = GetProcessHeap();
DWORD s = 1000;
LPVOID buf = HeapAlloc(h, HEAP_ZERO_MEMORY, s);
if (!buf)
return FALSE;
DWORD r = 0;
while (!DeviceIoControl(dev, code, in, ins, buf, s, &r, 0))
{
if (ERROR_INSUFFICIENT_BUFFER == GetLastError() || ERROR_MORE_DATA == GetLastError())
{
s *= 10;
LPVOID t = HeapReAlloc(h, HEAP_ZERO_MEMORY, buf, s);
if (!t)
{
HeapFree(h, 0, buf);
return FALSE;
}
buf = t;
}
else
{
printf("dic unk: %u\n", GetLastError());
HeapFree(h, 0, buf);
return FALSE;
}
}
*out = buf;
*outs = s;
return TRUE;
}
BOOL getvolptr(HANDLE volh, PRETRIEVAL_POINTERS_BUFFER* rpb, LPDWORD rpbs)
{
STARTING_VCN_INPUT_BUFFER vcn = { 0 };
return dic(volh, FSCTL_GET_RETRIEVAL_POINTERS, &vcn, sizeof(vcn), (void**)rpb, rpbs);
}
HANDLE fi = openfile("C:\\Windows\\System32\\Kernel32.dll");
if (INVALID_HANDLE_VALUE == fi)
{
printf("failed to open file! (%u)\n", GetLastError());
getchar();
}
else
{
PRETRIEVAL_POINTERS_BUFFER rpb = NULL;
DWORD rpbs = 0;
if (!getvolptr(fi, &rpb, &rpbs))
{
printf("failed to get vol ptrs! (%u)\n", GetLastError());
getchar();
}
else
{
ULONGLONG cluster_cnt = 0;
for (int i = 0; i < rpb->ExtentCount; ++i)
{
cluster_cnt = (ULONGLONG)(rpb->Extents[i].NextVcn.QuadPart) - (ULONGLONG)(rpb->StartingVcn.QuadPart);
printf("%d) size: %llu clusters (0x%016X)\n", i, cluster_cnt, rpb->Extents[i].Lcn.QuadPart);
}
HeapFree(GetProcessHeap(), 0, rpb);
}
closefile(fi);
}

Obtaining handle to key with NtCreateKey/NtOpenKey

PURPOSE
I'm trying to make a function which will create a given sub key in the HKCU registry hive, or open the sub key if it already exists, then return TRUE.
NOTES
Let RegSidPath represent a fully qualified HKCU registry path with an user SID appended to it such as \\Registry\\User\\S-1-5-20-xxxxxx-xxxxxx-xxxxxxxx-1050
Let KeyToCreate represent a specific registry path such as \\Software\\MyCompany\\MySoftware\\MySubKey
CODE
I have the following function:
BOOL CreateHKCUKey(PWCHAR RegSidPath, PWCHAR KeyToCreate) {
UNICODE_STRING uString;
RtlInitUnicodeString(&uString, RegSidPath);
OBJECT_ATTRIBUTES ObjAttributes;
InitializeObjectAttributes(&ObjAttributes, &uString, OBJ_CASE_INSENSITIVE, 0, 0);
HANDLE BaseKeyHandle = NULL;
NTSTATUS Status = NtOpenKey(&BaseKeyHandle, KEY_CREATE_SUB_KEY, &ObjAttributes);
if (NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
UNICODE_STRING KeyString = { 0 };
do {
PWCHAR NextSubKey = StrStrW((KeyString.Length == 0 ? KeyToCreate : KeyString.Buffer) + 1, L"\\");
DWORD CurrentKeyLength = lstrlenW(KeyToCreate) - lstrlenW(NextSubKey);
PWCHAR CurrentSubKey = PWCHAR(GlobalAlloc(GPTR, CurrentKeyLength + sizeof(WCHAR)));
if (CurrentSubKey != ERROR) {
memcpy(CurrentSubKey, KeyToCreate, CurrentKeyLength * sizeof(WCHAR));
CurrentSubKey[CurrentKeyLength] = UNICODE_NULL;
RtlInitUnicodeString(&KeyString, CurrentSubKey);
OBJECT_ATTRIBUTES KeyAttributes;
InitializeObjectAttributes(&KeyAttributes, &KeyString, OBJ_CASE_INSENSITIVE, &BaseKeyHandle, 0);
HANDLE CurrentHiveEntry = NULL;
Status = NtOpenKey(&CurrentHiveEntry, KEY_CREATE_SUB_KEY, &KeyAttributes);
if (RtlNtStatusToDosError(Status) == ERROR_BAD_PATHNAME) {
InitializeObjectAttributes(&KeyAttributes, &KeyString, OBJ_CASE_INSENSITIVE, &CurrentHiveEntry, 0);
DWORD DefaultDisposition;
Status = NtCreateKey(&CurrentHiveEntry, KEY_CREATE_SUB_KEY, &KeyAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, &DefaultDisposition);
if (NT_SUCCESS(Status)) {
if (StrCmpNW(KeyString.Buffer + uString.Length, KeyString.Buffer, lstrlenW(KeyToCreate) == 0))
return TRUE;
else continue;
} else break;
} else break;
BaseKeyHandle = CurrentHiveEntry;
}
} while (TRUE);
}
NtClose(BaseKeyHandle);
return FALSE;
}
PROBLEM
Whenever the code gets to this part of the function
Status = NtOpenKey(&CurrentHiveEntry, KEY_CREATE_SUB_KEY, &KeyAttributes);
if (RtlNtStatusToDosError(Status) == ERROR_BAD_PATHNAME) {
The return value is always ERROR_BAD_PATHNAME (161) even if the current sub key already exists.
QUESTION
What is the reason, and what am I doing wrong? Is there anything that I've done which is not correct, and how can I fix it?
NTSTATUS CreateKey(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, PWCHAR RegSidPath, PWCHAR KeyToCreate, PULONG Disposition)
{
UNICODE_STRING ObjectName;
RtlInitUnicodeString(&ObjectName, RegSidPath);
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName ,OBJ_CASE_INSENSITIVE };
NTSTATUS status = ZwOpenKey(&oa.RootDirectory, KEY_CREATE_SUB_KEY, &oa);
if (0 <= status)
{
ObjectName.Buffer = KeyToCreate;
do
{
ACCESS_MASK Access;
if (KeyToCreate = wcschr(++ObjectName.Buffer, '\\'))
{
ObjectName.Length = (USHORT)RtlPointerToOffset(ObjectName.Buffer, KeyToCreate);
Access = KEY_CREATE_SUB_KEY;
}
else
{
ObjectName.Length = (USHORT)wcslen(ObjectName.Buffer) * sizeof(WCHAR);
Access = DesiredAccess;
}
ObjectName.MaximumLength = ObjectName.Length;
status = ZwCreateKey(KeyHandle, Access, &oa, 0, 0, 0, Disposition);
NtClose(oa.RootDirectory);
oa.RootDirectory = *KeyHandle;
} while (0 <= status && (ObjectName.Buffer = KeyToCreate));
}
return status;
}
and use as
HANDLE hKey;
NTSTATUS status = CreateKey(&hKey, KEY_ALL_ACCESS,
L"\\REGISTRY\\USER\\S-***",
L"\\Software\\MyCompany\\MySoftware\\MySubKey", 0);
Unless you are writing a driver, use RegCreateKeyEx() instead. It handles all the logic of creating intermediate keys for you if they don't already exist. 1 function call, no looping needed.
HKEY hKey;
DWORD dwDisposition;
LONG lRet = RegCreateKeyExW(HKEY_USERS, L"S-1-5-20-xxxxxx-xxxxxx-xxxxxxxx-1050\\Software\\MyCompany\\MySoftware\\MySubKey", 0, NULL, REG_OPTION_NON_VOLATILE, samDesired, NULL, &hKey, &dwDisposition);
if (lRet == 0)
{
...
RegCloseKey(hKey);
}
However, to access the HKEY_CURRENT_USER hive of a specific user, the preferred solution is to use RegOpenCurrentUser() or LoadUserProfile() instead of accessing HKEY_USERS directly:
// impersonate the desired user first, then...
HKEY hRootKey;
LONG lRet = RegOpenCurrentUser(samDesired, &hRootKey);
if (lRet == 0)
{
HKEY hKey;
DWORD dwDisposition;
lRet = RegCreateKeyExW(hRootKey, L"Software\\MyCompany\\MySoftware\\MySubKey", 0, NULL, REG_OPTION_NON_VOLATILE, samDesired, NULL, &hKey, &dwDisposition);
if (lRet == 0)
{
...
RegCloseKey(hKey);
}
RegCloseKey(hRootKey);
}
// stop impersonating...
// obtain token to desired user first, then...
PROFILEINFO profile = {0};
profile.dwSize = sizeof(profile);
profile.dwFlags = PI_NOUI;
if (LoadUserProfile(hToken, &profile))
{
HKEY hKey;
DWORD dwDisposition;
LONG lRet = RegCreateKeyExW((HKEY)profile.hProfile, L"Software\\MyCompany\\MySoftware\\MySubKey", 0, NULL, REG_OPTION_NON_VOLATILE, samDesired, NULL, &hKey, &dwDisposition);
if (lRet == 0)
{
...
RegCloseKey(hKey);
}
UnloadUserProfile(hToken, profile.hProfile);
}
// release token ...

Unable to get the associated network UNC of a mapped drive C/C++

Have written a DLL that attempts to convert a mapped drive to its equivalent network UNC. But it fails when DLL is called by setup program running as an elevated process. As a potential fix I modified the example source offered by #RbMn from an answer to the following question: How to correctly detect Network drive when running with elevated privileges
The call to GetLogicalDrives worked just like it was stated it would. However when it makes a call to WNetGetConnection with a mapped drive letter the error returned is 1222 (ERROR_NO_NETWORK) thus not giving the associated UNC. I believe the problem stems from how I go about impersonating a logon. Since my knowledge of UAC matters is very limited, I am not certain what I have to alter about impersonating a logon in order to correctly get the information I need.
Any help is greatly appreciated.
Below is the actual code:
BOOL ConvertToMappedFolder(LPSTR pUncPath, LPSTR pMappedDrive)
{
BOOL bRet = 0;
if (1)
{
HANDLE hToken = NULL;
ULONG rcb = 0;
TOKEN_ELEVATION_TYPE tet = 0;
TOKEN_LINKED_TOKEN tlt = { 0 };
ULONG err = BOOL_TO_ERR(OpenProcessToken(GetCurrentProcess() /* NtCurrentProcess() */, TOKEN_QUERY, &hToken));
if (err == NOERROR)
{
err = BOOL_TO_ERR(GetTokenInformation(hToken, TokenElevationType, &tet, sizeof(tet), &rcb));
if (err == NOERROR)
{
if (tet == TokenElevationTypeFull)
{
err = BOOL_TO_ERR(GetTokenInformation(hToken, TokenLinkedToken, &tlt, sizeof(tlt), &rcb));
if (err == NOERROR)
{
if (NOERROR == (err = BOOL_TO_ERR(SetThreadToken(0, tlt.LinkedToken))))
{
bRet = ConvertToMappedFolderEx(pUncPath, pMappedDrive);
SetThreadToken(0, 0);
}
CloseHandle(tlt.LinkedToken);
}
}
}
}
}
BOOL ConvertToMappedFolderEx(LPSTR pUncPath, LPSTR pMappedDrive)
{
int nPos = 0;
UINT nType = 0;
char strDrive[MAX_PATH+1] = "?:\\";
DWORD dwDriveList = GetLogicalDrives();
BOOL bRet = FALSE;
(*pMappedDrive) = 0;
// Check each drive letter determining if it is a mapped drive...
while (dwDriveList)
{
if (dwDriveList & 1)
{
strDrive[0] = 0x41 + nPos;
nType = GetDriveType(strDrive);
// If drive unknown do not attempt to determine if its UNC matches up...
if (DRIVE_UNKNOWN != nType)
{
char szDeviceName[MAX_PATH+1] = "";
char szDriveLetter[4] = " :";
DWORD dwResult = 0;
DWORD cchBuff = MAX_PATH;
szDriveLetter[0] = strDrive[0];
dwResult = WNetGetConnection(szDriveLetter, (LPSTR) szDeviceName, &cchBuff);
if (NO_ERROR == dwResult)
{
LPSTR pPath = _stristr(pUncPath, szDeviceName);
if (NULL != pPath)
{
strcpy(pMappedDrive, szDriveLetter);
strcat(pMappedDrive, (pUncPath + strlen(szDeviceName)));
bRet = TRUE;
break;
}
}
}
}
dwDriveList >>= 1;
nPos++;
}
return (bRet);
}
connection made using Microsoft LAN Manager is per logon session. more exactly it associated with a logon session LUID. this is stored in token and can be read from TOKEN_STATISTICS.AuthenticationId. so result for any network drive functions depend from your current token - thread (if you impersonate) or process. use different tokens - can give different results. the elevated and not elevated processes always run in different logon sessions (have different AuthenticationId in process token).
so no sense speak about network drives in windows. need speak about network drives in logon session. and different logon session have different set of network drives. we can do next - enumerate all currently running processes, for every process open it token and query AuthenticationId - then we can once impersonate and query for every new AuthenticationId or say we can query AuthenticationId for not elevated token, linked to our elevated, and then try found process with exactly this token, impersonate it, and query with this token.
code example:
void CheckDrives()
{
LONG dwDriveList = GetLogicalDrives();
WCHAR Drive[] = L"Z:";
ULONG n = 1 + 'Z' - 'A';
do
{
if (_bittest(&dwDriveList, --n))
{
if (DRIVE_REMOTE == GetDriveTypeW(Drive))
{
PUSE_INFO_0 pui;
if (NET_API_STATUS err = NetUseGetInfo(0, Drive, 0, (BYTE**)&pui))
{
DbgPrint("%S error=%u\n", Drive, err);
}
else
{
DbgPrint("%S -> %S\n", pui->ui0_local, pui->ui0_remote);
NetApiBufferFree(pui);
}
}
}
--*Drive;
} while (n);
}
BOOL ImpersonateNotElevated(LUID AuthenticationId)
{
BOOL fOk = FALSE;
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32W pe = { sizeof(pe) };
ULONG rcb;
BOOL fFound = FALSE;
if (Process32First(hSnapshot, &pe))
{
do
{
if (HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ProcessID))
{
HANDLE hToken, hNewToken;
if (OpenProcessToken(hProcess, TOKEN_QUERY|TOKEN_DUPLICATE, &hToken))
{
TOKEN_STATISTICS ts;
if (GetTokenInformation(hToken, TokenStatistics, &ts, sizeof(ts), &rcb))
{
if (ts.AuthenticationId.LowPart == AuthenticationId.LowPart && ts.AuthenticationId.HighPart == AuthenticationId.HighPart)
{
fFound = TRUE;
if (DuplicateToken(hToken, SecurityImpersonation, &hNewToken))
{
fOk = SetThreadToken(0, hNewToken);
CloseHandle(hNewToken);
}
}
}
CloseHandle(hToken);
}
CloseHandle(hProcess);
}
} while (!fFound && Process32Next(hSnapshot, &pe));
}
CloseHandle(hSnapshot);
}
return fOk;
}
void CheckDrivesNotElevated()
{
HANDLE hToken;
if (OpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hToken))
{
union {
TOKEN_ELEVATION_TYPE tet;
TOKEN_LINKED_TOKEN tlt;
};
ULONG rcb;
if (GetTokenInformation(hToken, TokenElevationType, &tet, sizeof(tet), &rcb))
{
if (tet == TokenElevationTypeFull)
{
if (GetTokenInformation(hToken, TokenLinkedToken, &tlt, sizeof(tlt), &rcb))
{
TOKEN_STATISTICS ts;
BOOL fOk = GetTokenInformation(tlt.LinkedToken, ::TokenStatistics, &ts, sizeof(ts), &rcb);
CloseHandle(tlt.LinkedToken);
if (fOk)
{
if (ImpersonateNotElevated(ts.AuthenticationId))
{
CheckDrives();
SetThreadToken(0, 0);
}
}
}
}
else
{
CheckDrives();
}
}
CloseHandle(hToken);
}
}

Serial number enumeration failed when we cast Descriptor+Descriptor->SerialNumberOffset

I have a kernel driver which is used to find the serial number of storage devices, but there is an issue with the driver.
Descriptor->SerialNumberOffset is 103
but (LPCSTR)(UINT_PTR)Descriptor+(DWORD32)Descriptor->SerialNumberOffset is NULL
here is my code
NTSTATUS GetDeviceTypeAndUniqueID(IN PDEVICE_OBJECT StorageStackDeviceObject, cwDevices *lDeviceTypeArg, char *pszUidArg)
{
DWORRD lDeviceType=0;
STORAGE_PROPERTY_QUERY Query;
STORAGE_DEVICE_DESCRIPTOR Buffer[4];
NTSTATUS Status = STATUS_SUCCESS;
ULONG uBusType=BusTypeUnknown;
PSTORAGE_DEVICE_DESCRIPTOR Descriptor=NULL;
PIRP NewIrp2=NULL;
PIO_STACK_LOCATION NextIrpStack=NULL;
IO_STATUS_BLOCK IoStatus;
char szSptr[2]={'_','\0'};
Query.PropertyId = StorageDeviceProperty;// first set the query properties
Query.QueryType = PropertyStandardQuery;
lDeviceType=0;
if (KeGetCurrentIrql() > PASSIVE_LEVEL)
{
return STATUS_SUCCESS;
}
if(StorageStackDeviceObject == NULL)
{
return STATUS_SUCCESS;
}
if((StorageStackDeviceObject->DeviceType != FILE_DEVICE_DISK) &&
(StorageStackDeviceObject->DeviceType != FILE_DEVICE_CD_ROM)&&
(StorageStackDeviceObject->DeviceType != FILE_DEVICE_DVD)&&
(StorageStackDeviceObject->DeviceType !=FILE_DEVICE_TAPE) )
{
return STATUS_SUCCESS;
}
KeInitializeEvent(&WaitEvent_newIrp, NotificationEvent, TRUE);// initialize the waitable event
__try
{
NewIrp2=IoBuildDeviceIoControlRequest(IOCTL_STORAGE_QUERY_PROPERTY, StorageStackDeviceObject,
(PVOID)&Query,sizeof(STORAGE_PROPERTY_QUERY),
(PVOID)Buffer,sizeof(STORAGE_DEVICE_DESCRIPTOR)*4,
FALSE,&WaitEvent_newIrp,&IoStatus);
if(NewIrp2==NULL)
{
return STATUS_SUCCESS;
}
Status = IoCallDriver(StorageStackDeviceObject, NewIrp2);// send this irp to the storage device
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&WaitEvent_newIrp, Executive, KernelMode, FALSE, NULL);
Status =IoStatus.Status;
}
}
__finally
{
if(NT_SUCCESS(Status))
{
if (NT_SUCCESS(Status))
{
if(Buffer!=NULL)
{
char szStart[256];
Descriptor = (PSTORAGE_DEVICE_DESCRIPTOR)Buffer;
uBusType = Descriptor->BusType; //Get the bus type.
if(Descriptor->SerialNumberOffset!=0)//Is Valid SerialNumberOffset, returns 103
{
strcpy(szStart,(char*)(UINT_PTR)Descriptor+(DWORD32)Descriptor->SerialNumberOffset);
//szStart is null
}
}
NewIrp2 = NULL;
}
}
}
}
please share how to solve this
you mistake in OutputBufferLength - why you decide that sizeof(STORAGE_DEVICE_DESCRIPTOR)*4 is enough ? why not 5* sizeof(STORAGE_DEVICE_DESCRIPTOR) for example ? really what OutputBufferLength must be is unknown - you need call this query in loop and compare your current OutputBufferLength with STORAGE_DEVICE_DESCRIPTOR.Size - if OutputBufferLength < STORAGE_DEVICE_DESCRIPTOR.Size - you must again send IOCTL_STORAGE_QUERY_PROPERTY with OutputBufferLength = STORAGE_DEVICE_DESCRIPTOR.Size. the code can be look like.
STORAGE_PROPERTY_QUERY spq = { StorageDeviceProperty, PropertyStandardQuery };
union {
PVOID buf;
PSTR psz;
PSTORAGE_DEVICE_DESCRIPTOR psdd;
};
ULONG size = sizeof(STORAGE_DEVICE_DESCRIPTOR) + 0x100;
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
do
{
status = STATUS_INSUFFICIENT_RESOURCES;
if (buf = ExAllocatePool(PagedPool, size))
{
switch (status = (NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb,
IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), buf, size)))
{
case STATUS_SUCCESS:
case STATUS_BUFFER_OVERFLOW:
if (psdd->Version == sizeof(STORAGE_DEVICE_DESCRIPTOR))
{
if (psdd->Size > size)
{
size = psdd->Size;
status = STATUS_BUFFER_OVERFLOW;
}
else
{
if (psdd->SerialNumberOffset)
{
DbgPrint("SerialNumber = %s\n", psz + psdd->SerialNumberOffset);
}
}
}
else
{
status = STATUS_INVALID_PARAMETER;
}
break;
}
ExFreePool(buf);
}
} while (status == STATUS_BUFFER_OVERFLOW);
in place NtDeviceIoControlFile we of course can use IoBuildDeviceIoControlRequest + IoCallDriver - this nothing change and unrelated to problem

Trouble using Windows MIDI API (no callbacks when playing)

I have an USB-connected MIDI-keyboard. It works fine in other applications. However, in my own program it does not. The midiInOpen() call goes through, I get one callback (from opening the device) but I don't get any callbacks when playing the keyboard.
By using midiInGetDevCaps() I can see that I'm using the correct device.
Any ideas? Could the other (commercial) applications use some other API?
static void CALLBACK myFunc(HMIDIIN handle, UINT uMsg,
DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) {
printf("Callback!"\n);
}
int main() {
unsigned long result;
HMIDIIN inHandle;
result = midiInOpen(&inHandle, 0, (DWORD)myFunc, 0, CALLBACK_FUNCTION);
if (result)
{
printf("There was an error opening MIDI\n");
}
while(1) { Sleep(1); }
}
You need to call midiInstart. You also need to pump messages. Nothing is going to happen if you're calling Sleep and not servicing input.
Here's a bit of a tutorial on Windows MIDI.
Here's an extract of a class (WinMidiIn) I wrote for Win MIDI input (lots of error handling removed, class member types can be inferred from the calls they are passed to as params):
{
MMRESULT res = ::midiInOpen(&mMidiIn, mDeviceIdx, (DWORD_PTR)MidiInCallbackProc, (DWORD_PTR)this,
CALLBACK_FUNCTION | MIDI_IO_STATUS);
if (MMSYSERR_NOERROR != res)
return;
const int kDataBufLen = 512;
int idx;
for (idx = 0; idx < MIDIHDR_CNT; ++idx)
{
mMidiHdrs[idx].lpData = (LPSTR) ::malloc(kDataBufLen);
mMidiHdrs[idx].dwBufferLength = kDataBufLen;
res = ::midiInPrepareHeader(mMidiIn, &mMidiHdrs[idx], (UINT)sizeof(MIDIHDR));
res = ::midiInAddBuffer(mMidiIn, &mMidiHdrs[idx], sizeof(MIDIHDR));
}
res = ::midiInStart(mMidiIn);
for (;;)
{
DWORD result;
MSG msg;
// Read all of the messages in this next loop,
// removing each message as we read it.
while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// If it is a quit message, exit.
if (msg.message == WM_QUIT)
break;
// Otherwise, dispatch the message.
::DispatchMessage(&msg);
}
// Wait for any message sent or posted to this queue
// or for one of the passed handles be set to signaled.
result = ::MsgWaitForMultipleObjects(1, &mDoneEvent, FALSE, INFINITE, QS_ALLINPUT);
// The result tells us the type of event we have.
if (result == (WAIT_OBJECT_0 + 1))
{
// New messages have arrived.
// Continue to the top of the always while loop to
// dispatch them and resume waiting.
continue;
}
else if (WAIT_TIMEOUT == result)
continue;
else if (WAIT_OBJECT_0 == result)
break; // done event fired
else
break; // ??
}
res = ::midiInReset(mMidiIn);
for (idx = 0; idx < MIDIHDR_CNT; ++idx)
{
res = ::midiInUnprepareHeader(mMidiIn, &mMidiHdrs[idx], (UINT)sizeof(MIDIHDR));
::free(mMidiHdrs[idx].lpData);
mMidiHdrs[idx].lpData = NULL;
}
res = ::midiInClose(mMidiIn);
mMidiIn = NULL;
}
void CALLBACK
MidiInCallbackProc(HMIDIIN hmi,
UINT wMsg,
DWORD dwInstance,
DWORD dwParam1,
DWORD dwParam2)
{
MMRESULT res;
LPMIDIHDR hdr;
WinMidiIn * _this = (WinMidiIn *) dwInstance;
switch (wMsg)
{
case MIM_DATA:
// dwParam1 is the midi event with status in the low byte of the low word
// dwParam2 is the event time in ms since the start of midi in
// data: LOBYTE(dwParam1), HIBYTE(dwParam1), LOBYTE(HIWORD(dwParam1))
break;
case MIM_ERROR:
break;
case MIM_LONGDATA:
// dwParam1 is the lpMidiHdr
// dwParam2 is the event time in ms since the start of midi in
hdr = (LPMIDIHDR) dwParam1;
// sysex: (byte*)hdr->lpData, (int)hdr->dwBytesRecorded
res = ::midiInAddBuffer(_this->mMidiIn, hdr, sizeof(MIDIHDR));
break;
case MIM_LONGERROR:
hdr = (LPMIDIHDR) dwParam1;
res = ::midiInAddBuffer(_this->mMidiIn, hdr, sizeof(MIDIHDR));
break;
}
}
You can find an example here https://gist.github.com/yoggy/1485181
I post the code here in case the link becomes dead
You may be missing the midiInStart()
#include <SDKDDKVer.h>
#include <Windows.h>
#include <stdio.h>
#include <conio.h>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")
void PrintMidiDevices()
{
UINT nMidiDeviceNum;
MIDIINCAPS caps;
nMidiDeviceNum = midiInGetNumDevs();
if (nMidiDeviceNum == 0) {
fprintf(stderr, "midiInGetNumDevs() return 0...");
return;
}
printf("== PrintMidiDevices() == \n");
for (unsigned int i = 0; i < nMidiDeviceNum; ++i) {
midiInGetDevCaps(i, &caps, sizeof(MIDIINCAPS));
printf("\t%d : name = %s\n", i, caps.szPname);
}
printf("=====\n");
}
void CALLBACK MidiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
switch(wMsg) {
case MIM_OPEN:
printf("wMsg=MIM_OPEN\n");
break;
case MIM_CLOSE:
printf("wMsg=MIM_CLOSE\n");
break;
case MIM_DATA:
printf("wMsg=MIM_DATA, dwInstance=%08x, dwParam1=%08x, dwParam2=%08x\n", dwInstance, dwParam1, dwParam2);
break;
case MIM_LONGDATA:
printf("wMsg=MIM_LONGDATA\n");
break;
case MIM_ERROR:
printf("wMsg=MIM_ERROR\n");
break;
case MIM_LONGERROR:
printf("wMsg=MIM_LONGERROR\n");
break;
case MIM_MOREDATA:
printf("wMsg=MIM_MOREDATA\n");
break;
default:
printf("wMsg = unknown\n");
break;
}
return;
}
int main(int argc, char* argv[])
{
HMIDIIN hMidiDevice = NULL;;
DWORD nMidiPort = 0;
UINT nMidiDeviceNum;
MMRESULT rv;
PrintMidiDevices();
nMidiDeviceNum = midiInGetNumDevs();
if (nMidiDeviceNum == 0) {
fprintf(stderr, "midiInGetNumDevs() return 0...");
return -1;
}
rv = midiInOpen(&hMidiDevice, nMidiPort, (DWORD)(void*)MidiInProc, 0, CALLBACK_FUNCTION);
if (rv != MMSYSERR_NOERROR) {
fprintf(stderr, "midiInOpen() failed...rv=%d", rv);
return -1;
}
midiInStart(hMidiDevice);
while(true) {
if (!_kbhit()) {
Sleep(100);
continue;
}
int c = _getch();
if (c == VK_ESCAPE) break;
if (c == 'q') break;
}
midiInStop(hMidiDevice);
midiInClose(hMidiDevice);
hMidiDevice = NULL;
return 0;
}

Resources