Related
I'm trying to start a process in the interactive session of the currently logged-on user using Win32 API. The start-up is taking place from a remote SSH session, so this should be similar to starting a process in an interactive session from a service. The remote SSH session is logged-in with the same user that I'm trying to start-up a process in the interactive session for.
I have the following setup:
A Windows Server 2019 GCE VM
OpenSSH server is enabled on the server machine
A user administrator that has the following privileges:
Act as the operating system administrator
Impersonate a client after authentication
I use the following piece of C# code to start-up the process, in this case it's notepad.exe. The code is opening the token of explorer.exe process which should run in the user session. The token is then duplicated and used to create the process.
var pHandle = OpenProcess(
0x001F0FFF,
false,
explorerPID);
if (pHandle == IntPtr.Zero) { // handle win32 error }
if (!OpenProcessToken(pHandle,
TOKEN_ALL_ACCESS,
out IntPtr tHandle)) {
// handle win32 error
}
var lpT = new SECURITY_ATTRIBUTES();
if (!DuplicateTokenEx(
tHandle,
TOKEN_ALL_ACCESS,
ref lpT,
SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
TOKEN_TYPE.TokenPrimary,
out IntPtr newtHandle)) {
// handle win32 error
}
var startInfo = new STARTUPINFO();
var lpProcAttr = new SECURITY_ATTRIBUTES();
var lpThreadAttr = new SECURITY_ATTRIBUTES();
startInfo.lpDesktop = "winsta0\\default";
startInfo.cb = Marshal.SizeOf(startInfo);
if (!CreateProcessAsUser(newtHandle,
"c:\\Windows\\notepad.exe",
null,
ref lpProcAttr,
ref lpThreadAttr,
false,
0x00000020,
IntPtr.Zero,
null,
ref startInfo,
out _)) {
// handle win32 error
}
I am deploying the binary on the server and then via SSH I'm executing it. The problem is I'm getting (5) Access is denied on the CreateProcessAsUser function. Does anyone have any clue of what I'm missing here or if it's even possible to achieve this?
for call CreateProcessAsUser in general case you need SE_ASSIGNPRIMARYTOKEN_NAME privilege. also you can search for some process in some session (like explorer) and use it token. or you can enumerate terminal sessions and get token from session. for this you need TCB privilege. both this possible got you you initially have Debug privileges. code can be next:
#define BEGIN_PRIVILEGES(name, n) static const union { TOKEN_PRIVILEGES name;\
struct { ULONG PrivilegeCount; LUID_AND_ATTRIBUTES Privileges[n];} label(_) = { n, {
#define LAA(se) {{se}, SE_PRIVILEGE_ENABLED }
#define LAA_D(se) {{se} }
#define END_PRIVILEGES }};};
const SECURITY_QUALITY_OF_SERVICE sqos = {
sizeof (sqos), SecurityImpersonation, SECURITY_DYNAMIC_TRACKING, FALSE
};
const OBJECT_ATTRIBUTES oa_sqos = { sizeof(oa_sqos), 0, 0, 0, 0, const_cast<SECURITY_QUALITY_OF_SERVICE*>(&sqos) };
const TOKEN_PRIVILEGES tp_Debug = { 1, { { { SE_DEBUG_PRIVILEGE }, SE_PRIVILEGE_ENABLED } } };
BEGIN_PRIVILEGES(tp_TCB, 3)
LAA(SE_TCB_PRIVILEGE),
LAA(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE),
LAA(SE_INCREASE_QUOTA_PRIVILEGE),
END_PRIVILEGES
NTSTATUS GetToken(PVOID buf, const TOKEN_PRIVILEGES* RequiredSet)
{
NTSTATUS status;
union {
PVOID pv;
PBYTE pb;
PSYSTEM_PROCESS_INFORMATION pspi;
};
pv = buf;
ULONG NextEntryOffset = 0;
do
{
pb += NextEntryOffset;
HANDLE hProcess, hToken, hNewToken;
CLIENT_ID ClientId = { pspi->UniqueProcessId };
if (ClientId.UniqueProcess)
{
if (0 <= NtOpenProcess(&hProcess, PROCESS_QUERY_LIMITED_INFORMATION,
const_cast<POBJECT_ATTRIBUTES>(&oa_sqos), &ClientId))
{
status = NtOpenProcessToken(hProcess, TOKEN_DUPLICATE, &hToken);
NtClose(hProcess);
if (0 <= status)
{
status = NtDuplicateToken(hToken, TOKEN_ADJUST_PRIVILEGES|TOKEN_IMPERSONATE,
const_cast<POBJECT_ATTRIBUTES>(&oa_sqos), FALSE, TokenImpersonation, &hNewToken);
NtClose(hToken);
if (0 <= status)
{
status = NtAdjustPrivilegesToken(hNewToken, FALSE, const_cast<PTOKEN_PRIVILEGES>(RequiredSet), 0, 0, 0);
if (STATUS_SUCCESS == status)
{
status = NtSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, &hNewToken, sizeof(hNewToken));
}
NtClose(hNewToken);
if (STATUS_SUCCESS == status)
{
return STATUS_SUCCESS;
}
}
}
}
}
} while (NextEntryOffset = pspi->NextEntryOffset);
return STATUS_UNSUCCESSFUL;
}
NTSTATUS AdjustPrivileges(_In_ const TOKEN_PRIVILEGES* ptp)
{
NTSTATUS status;
HANDLE hToken, hNewToken;
if (0 <= (status = NtOpenProcessToken(NtCurrentProcess(), TOKEN_DUPLICATE, &hToken)))
{
status = NtDuplicateToken(hToken, TOKEN_ADJUST_PRIVILEGES|TOKEN_IMPERSONATE,
const_cast<OBJECT_ATTRIBUTES*>(&oa_sqos), FALSE, TokenImpersonation, &hNewToken);
NtClose(hToken);
if (0 <= status)
{
if (STATUS_SUCCESS == (status = NtAdjustPrivilegesToken(hNewToken, FALSE,
const_cast<PTOKEN_PRIVILEGES>(ptp), 0, 0, 0)))
{
status = NtSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, &hNewToken, sizeof(hNewToken));
}
NtClose(hNewToken);
}
}
return status;
}
NTSTATUS ImpersonateToken(_In_ const TOKEN_PRIVILEGES* RequiredSet)
{
NTSTATUS status = AdjustPrivileges(&tp_Debug);
ULONG cb = 0x40000;
do
{
status = STATUS_INSUFFICIENT_RESOURCES;
if (PBYTE buf = new BYTE[cb += PAGE_SIZE])
{
if (0 <= (status = NtQuerySystemInformation(SystemProcessInformation, buf, cb, &cb)))
{
status = GetToken(buf, RequiredSet);
if (status == STATUS_INFO_LENGTH_MISMATCH)
{
status = STATUS_UNSUCCESSFUL;
}
}
delete [] buf;
}
} while(status == STATUS_INFO_LENGTH_MISMATCH);
return status;
}
void StartNotepadInSession(ULONG dwSessionId)
{
HANDLE hToken;
WCHAR sz[MAX_PATH];
if (SearchPathW(0, L"notepad.exe", 0, _countof(sz), sz, 0))
{
if (WTSQueryUserToken(dwSessionId, &hToken))
{
PVOID lpEnvironment;
if (CreateEnvironmentBlock(&lpEnvironment, hToken, FALSE))
{
PROCESS_INFORMATION pi;
STARTUPINFOW si = { sizeof(si) };
if (CreateProcessAsUserW(hToken, sz, 0, 0, 0, 0,
CREATE_UNICODE_ENVIRONMENT, lpEnvironment, 0, &si, &pi))
{
NtClose(pi.hThread);
NtClose(pi.hProcess);
}
DestroyEnvironmentBlock(lpEnvironment);
}
NtClose(hToken);
}
}
}
void exec()
{
if (0 <= ImpersonateToken(&tp_TCB))
{
ULONG MySessionId, SessionId;
ProcessIdToSessionId(GetCurrentProcessId(), &MySessionId);
PWTS_SESSION_INFOW pSessionInfo;
ULONG Count;
if (WTSEnumerateSessionsW(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &Count))
{
DbgPrint("Sessions = %x\r\n", Count);
if (Count)
{
pSessionInfo += Count;
do
{
--pSessionInfo;
DbgPrint("SESSION_INFO<%x>: %x %S\r\n", pSessionInfo->SessionId, pSessionInfo->State, pSessionInfo->pWinStationName);
if (SessionId = pSessionInfo->SessionId)
{
switch (pSessionInfo->State)
{
case WTSDisconnected:
case WTSActive:
if (MySessionId != SessionId)
{
StartNotepadInSession(SessionId);
}
break;
}
}
} while (--Count);
}
WTSFreeMemory(pSessionInfo);
}
RevertToSelf();
}
}
I have a volume path \\?\USBSTOR#Disk&Ven_SanDisk&Prod_Ultra_Fit&Rev_1.00#4C530000260829120162&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b} and I want to determine the dos location(c:/d:/...) of the path.
I get the path from :
PDEV_BROADCAST_DEVICEINTERFACE lpdbv = (PDEV_BROADCAST_DEVICEINTERFACE)lpdb;
lpdbv->dbcc_name <---path
How can I get the drive letter from this?
Will the function
FilterGetDosName(LPCWSTR lpVolumeName,
LPWSTR lpDosName,
DWORD dwDosNameBufferSize)
help in anyway?
for this you need
(1) open file on device
(2) query it device name via IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
this ioctl have no input buffer and return
The mount manager client returns a variable-length structure of type
MOUNTDEV_NAME, defined in Mountmgr.h
and
If the output buffer is too small to hold the device name, the mount
manager client must set the Information field to
sizeof(MOUNTDEV_NAME) and the Status field to
STATUS_BUFFER_OVERFLOW. In addition, the mount manager client fills in the NameLength member of the MOUNTDEV_NAME
structure.
STATUS_BUFFER_OVERFLOW converted to ERROR_MORE_DATA win32 error. if DeviceIoControl return this error we can get required size of buffer as
FIELD_OFFSET(MOUNTDEV_NAME, Name) + pmdn->NameLength;
(3) query mount manager device with IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH
this ioct take as input MOUNTDEV_NAME which we got on step 2 and return MOUNTMGR_VOLUME_PATHS (read about this inside mountmgr.h)
so final code
#include <mountmgr.h>
inline ULONG BOOL_TO_ERROR(BOOL f)
{
return f ? NOERROR : GetLastError();
}
ULONG GetDosVolumePath(PCWSTR InterfaceLink, PWSTR* ppszDosPath)
{
*ppszDosPath = 0;
HANDLE hFile = CreateFileW(InterfaceLink, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hFile == INVALID_HANDLE_VALUE) return GetLastError();
union {
PVOID buf;
PMOUNTDEV_NAME pmdn;
};
ULONG dwError;
PVOID stack = alloca(guz);
ULONG cb = 0, rcb = sizeof(MOUNTDEV_NAME) + 0x10, InputBufferLength;
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
dwError = BOOL_TO_ERROR(DeviceIoControl(hFile, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, 0, 0, pmdn, cb, &rcb, 0));
rcb = FIELD_OFFSET(MOUNTDEV_NAME, Name) + pmdn->NameLength;
} while ( dwError == ERROR_MORE_DATA);
CloseHandle(hFile);
if (dwError == NOERROR)
{
hFile = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, 0, 0, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
union {
PVOID pv;
PMOUNTMGR_VOLUME_PATHS pmvp;
};
InputBufferLength = rcb, cb = 0, rcb = sizeof(MOUNTMGR_VOLUME_PATHS) + 0x4;
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(pv = alloca(rcb - cb), pmdn);
}
dwError = BOOL_TO_ERROR(DeviceIoControl(hFile,
IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH,
pmdn, InputBufferLength, pmvp, cb, &rcb, 0));
if (dwError == NOERROR)
{
*ppszDosPath = _wcsdup(pmvp->MultiSz);
}
rcb = FIELD_OFFSET(MOUNTMGR_VOLUME_PATHS, MultiSz) + pmvp->MultiSzLength;
} while (dwError == ERROR_MORE_DATA);
CloseHandle(hFile);
}
}
return dwError;
}
PWSTR pszDosPath;
if (GetDosVolumePath(L"\\\\?\\USBSTOR#Disk&Ven_SanDisk&Prod_Ultra_Fit&Rev_1.00#4C530000260829120162&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}", &pszDosPath) == NOERROR)
{
DbgPrint("%S\n", pszDosPath);
free(pszDosPath);
}
I need to get the following information for all of the physical disks in the system when Windows 10 storage spaces is enabled.
Model
Serial Number
Firmware Version
Capacity
Index of the Disk
Pnp Id of the disk (to get the SCSI controller name using CM_Get_Parent)
Location information (Bus number, Target Id and LUN)
What I have tried so far:
Used WMI class MSFT_PhysicalDisk
Although this class gives me the adapter number (so i can do without the disk PNP), the location information it gives is not complete when a disk is connected to a different PCI storage controller (such as the Marvell 92xx SATA 6g controller).
Used SetupDiGetClassDevs with GUID_DEVINTERFACE_DISK, passed the handle to SetupDiGetDeviceInterface and used SetupDiGetDeviceInterfaceDetail for location information (Bus/Target Id/LUN), PNP Id, and Device Path. I can pass the device path to CreateFile and get the rest of the information (similar to this approach). The problem with this is that it does not give me all the physical disks. The disks under the storage spaces pool are omitted.
Use an approach similar to the second one but instead of SetupDiGetDeviceInterface and SetupDiGetDeviceInterfaceDetail, use SetupDiEnumDeviceInfo and CM_Get_DevNode_Registry_Property (using the Disk Drives Guid from here). Although this gives me the location and PNP id for all of the physical disks, I can't use anything here (that I know of) to call CreateFile and get the rest of the details.
How can I get the above details for each of the physical disks when storage spaces is enabled?
As a side note, if there is a way to get the disk PNP id from the disk index using the CreateFile and DeviceIoControl, that can also be very helpful for me.
at first we need enumerate all disks in system by call CM_Get_Device_Interface_ListW and CM_Get_Device_Interface_List_SizeW with GUID_DEVINTERFACE_DISK
#include <Shlwapi.h>
#include <cfgmgr32.h>
#undef _NTDDSTOR_H_
#include <ntddstor.h>
#include <ntdddisk.h>
static volatile UCHAR guz;
CONFIGRET EnumDisks(PCSTR prefix, PGUID InterfaceClassGuid)
{
CONFIGRET err;
PVOID stack = alloca(guz);
ULONG BufferLen = 0, NeedLen = 256;
union {
PVOID buf;
PWSTR pszDeviceInterface;
};
for(;;)
{
if (BufferLen < NeedLen)
{
BufferLen = RtlPointerToOffset(buf = alloca((NeedLen - BufferLen) * sizeof(WCHAR)), stack) / sizeof(WCHAR);
}
switch (err = CM_Get_Device_Interface_ListW(InterfaceClassGuid,
0, pszDeviceInterface, BufferLen, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
{
case CR_BUFFER_SMALL:
if (err = CM_Get_Device_Interface_List_SizeW(&NeedLen, InterfaceClassGuid,
0, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
{
default:
return err;
}
continue;
case CR_SUCCESS:
while (*pszDeviceInterface)
{
DbgPrint("Interface=[%S]\n", pszDeviceInterface);
HANDLE hFile = CreateFileW(pszDeviceInterface, FILE_GENERIC_READ,
FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
GetDiskPropertyByHandle(hFile);
CloseHandle(hFile);
}
GetPropertyByInterface(prefix, pszDeviceInterface);
pszDeviceInterface += 1 + wcslen(pszDeviceInterface);
}
return CR_SUCCESS;
}
}
}
CONFIGRET EnumDisks()
{
char prefix[256];
memset(prefix, '\t', sizeof(prefix));
prefix[sizeof(prefix) - 1] = 0;
prefix[0] = 0;
return EnumDisks(prefix + sizeof(prefix) - 1, const_cast<PGUID>(&GUID_DEVINTERFACE_DISK));
}
CM_Get_Device_Interface_ListW return multiple, NULL-terminated Unicode strings, each representing the symbolic link name of an interface instance.
from one side this symbolic link name can be passed to CreateFileW for open disk device. after this we can set some ioctl to disk - for get
Index of the Disk
Capacity
Serial Number
Partition Information
example:
void GetDiskPropertyByHandle(HANDLE hDisk)
{
HANDLE hPartition;
IO_STATUS_BLOCK iosb;
STORAGE_DEVICE_NUMBER sdn;
GET_LENGTH_INFORMATION li;
NTSTATUS status = NtDeviceIoControlFile(hDisk, 0, 0, 0, &iosb,
IOCTL_STORAGE_GET_DEVICE_NUMBER, 0, 0, &sdn, sizeof(sdn));
if (0 <= status && sdn.DeviceType == FILE_DEVICE_DISK && !sdn.PartitionNumber)
{
DbgPrint("\\Device\\Harddisk%d\n", sdn.DeviceNumber);
WCHAR sz[64], *c = sz + swprintf(sz, L"\\Device\\Harddisk%d\\Partition", sdn.DeviceNumber);
WCHAR szSize[32];
if (0 <= (status = NtDeviceIoControlFile(hDisk, 0, 0, 0, &iosb,
IOCTL_DISK_GET_LENGTH_INFO, 0, 0, &li, sizeof(li))))
{
DbgPrint("Length = %S (%I64x)\n",
StrFormatByteSizeW(li.Length.QuadPart, szSize, RTL_NUMBER_OF(szSize)),
li.Length.QuadPart);
}
UNICODE_STRING ObjectName;
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName, OBJ_CASE_INSENSITIVE };
PVOID stack = alloca(guz);
union {
PVOID buf;
PDRIVE_LAYOUT_INFORMATION_EX pdli;
PSTORAGE_DEVICE_DESCRIPTOR psdd;
PCSTR psz;
};
STORAGE_PROPERTY_QUERY spq = { StorageDeviceProperty, PropertyStandardQuery };
ULONG cb = 0, rcb = sizeof(STORAGE_DEVICE_DESCRIPTOR) + 0x40, PartitionCount = 4;
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
switch (status = (NtDeviceIoControlFile(hDisk, 0, 0, 0, &iosb,
IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), buf, cb)))
{
case STATUS_SUCCESS:
case STATUS_BUFFER_OVERFLOW:
if (psdd->Version == sizeof(STORAGE_DEVICE_DESCRIPTOR))
{
if (psdd->Size > cb)
{
rcb = psdd->Size;
status = STATUS_BUFFER_OVERFLOW;
}
else
{
if (psdd->SerialNumberOffset)
{
DbgPrint("SerialNumber = %s\n", psz + psdd->SerialNumberOffset);
}
}
}
else
{
status = STATUS_INVALID_PARAMETER;
}
break;
}
} while (status == STATUS_BUFFER_OVERFLOW);
for (;;)
{
if (cb < (rcb = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[PartitionCount])))
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (0 <= (status = NtDeviceIoControlFile(hDisk, 0, 0, 0, &iosb,
IOCTL_DISK_GET_DRIVE_LAYOUT_EX, 0, 0, buf, cb)))
{
if (PartitionCount = pdli->PartitionCount)
{
PPARTITION_INFORMATION_EX PartitionEntry = pdli->PartitionEntry;
do
{
if (!PartitionEntry->PartitionNumber)
{
continue;
}
_itow(PartitionEntry->PartitionNumber, c, 10);
RtlInitUnicodeString(&ObjectName, sz);
DbgPrint("%wZ\nOffset=%S ", &ObjectName,
StrFormatByteSizeW(PartitionEntry->StartingOffset.QuadPart, szSize, RTL_NUMBER_OF(szSize)));
DbgPrint("Length=%S\n",
StrFormatByteSizeW(PartitionEntry->PartitionLength.QuadPart, szSize, RTL_NUMBER_OF(szSize)));
char PartitionName[256], *szPartitionName;
switch (PartitionEntry->PartitionStyle)
{
case PARTITION_STYLE_MBR:
DbgPrint("MBR: type=%x boot=%x", PartitionEntry->Mbr.PartitionType, PartitionEntry->Mbr.BootIndicator);
break;
case PARTITION_STYLE_GPT:
if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_ENTRY_UNUSED_GUID))
{
szPartitionName = "UNUSED";
}
else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_SYSTEM_GUID))
{
szPartitionName = "SYSTEM";
}
else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_MSFT_RESERVED_GUID))
{
szPartitionName = "RESERVED";
}
else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_BASIC_DATA_GUID))
{
szPartitionName = "DATA";
}
else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_MSFT_RECOVERY_GUID))
{
szPartitionName = "RECOVERY";
}
else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_MSFT_SNAPSHOT_GUID))
{
szPartitionName = "SNAPSHOT";
}
else
{
sprintf(szPartitionName = PartitionName, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
PartitionEntry->Gpt.PartitionType.Data1,
PartitionEntry->Gpt.PartitionType.Data2,
PartitionEntry->Gpt.PartitionType.Data3,
PartitionEntry->Gpt.PartitionType.Data4[0],
PartitionEntry->Gpt.PartitionType.Data4[1],
PartitionEntry->Gpt.PartitionType.Data4[2],
PartitionEntry->Gpt.PartitionType.Data4[3],
PartitionEntry->Gpt.PartitionType.Data4[4],
PartitionEntry->Gpt.PartitionType.Data4[5],
PartitionEntry->Gpt.PartitionType.Data4[6],
PartitionEntry->Gpt.PartitionType.Data4[7]);
}
DbgPrint("[%s] %I64x \"%S\"",
szPartitionName,
PartitionEntry->Gpt.Attributes,
PartitionEntry->Gpt.Name);
break;
}
if (0 <= NtOpenFile(&hPartition, FILE_GENERIC_READ, &oa, &iosb,
FILE_SHARE_VALID_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT))
{
union {
BYTE bb[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 32*sizeof(WCHAR) ];
FILE_FS_ATTRIBUTE_INFORMATION ffai;
};
switch (NtQueryVolumeInformationFile(hPartition, &iosb, &ffai, sizeof(bb), FileFsAttributeInformation))
{
case STATUS_SUCCESS:
case STATUS_BUFFER_OVERFLOW:
DbgPrint(" \"%.*S\"\n", ffai.FileSystemNameLength >> 1 , ffai.FileSystemName);
break;
}
NtClose(hPartition);
}
} while (PartitionEntry++, --PartitionCount);
}
return ;
}
switch (status)
{
case STATUS_BUFFER_OVERFLOW:
PartitionCount = pdli->PartitionCount;
continue;
case STATUS_INFO_LENGTH_MISMATCH:
case STATUS_BUFFER_TOO_SMALL:
PartitionCount <<= 1;
continue;
default:
return ;
}
}
}
}
from another size we can get Device Instance ID from interface string by call CM_Get_Device_Interface_PropertyW with DEVPKEY_Device_InstanceId. after this we call CM_Locate_DevNodeW for get device instance handle.
CONFIGRET GetPropertyByInterface(PCSTR prefix, PCWSTR pszDeviceInterface)
{
ULONG cb = 0, rcb = 256;
PVOID stack = alloca(guz);
DEVPROPTYPE PropertyType;
CONFIGRET status;
union {
PVOID pv;
PWSTR DeviceID;
PBYTE pb;
};
do
{
if (cb < rcb)
{
rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
}
status = CM_Get_Device_Interface_PropertyW(pszDeviceInterface, &DEVPKEY_Device_InstanceId, &PropertyType, pb, &rcb, 0);
if (status == CR_SUCCESS)
{
if (PropertyType == DEVPROP_TYPE_STRING)
{
DbgPrint("%sDeviceID = %S\n", prefix, DeviceID);
DEVINST dnDevInst;
if (CR_SUCCESS == (status = CM_Locate_DevNodeW(&dnDevInst, DeviceID, CM_LOCATE_DEVNODE_NORMAL)))
{
GetPropertyByDeviceID(prefix, dnDevInst);
}
}
else
{
status = CR_WRONG_TYPE;
}
break;
}
} while (status == CR_BUFFER_SMALL);
return status;
}
with device instance handle we can query many device properties via CM_Get_DevNode_PropertyW like:
DEVPKEY_Device_LocationInfo, DEVPKEY_NAME, DEVPKEY_Device_PDOName, DEVPKEY_Device_FirmwareVersion, DEVPKEY_Device_Model, DEVPKEY_Device_DriverVersion and many others - look full list in devpkey.h
finally we can call CM_Get_Parent and recursive query all this properties for parent device(s) until we not rich top of stack:
#define OPEN_PDO
void GetPropertyByDeviceID(PCSTR prefix, DEVINST dnDevInst)
{
#ifdef OPEN_PDO
HANDLE hFile;
IO_STATUS_BLOCK iosb;
UNICODE_STRING ObjectName;
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName, OBJ_CASE_INSENSITIVE };
#endif
CONFIGRET status;
ULONG cb = 0, rcb = 0x80;
PVOID stack = alloca(guz);
DEVPROPTYPE PropertyType;
union {
PVOID pv;
PWSTR sz;
PBYTE pb;
};
static struct
{
CONST DEVPROPKEY *PropertyKey;
PCWSTR PropertyName;
} PropertyKeys[] = {
{ &DEVPKEY_Device_PDOName, L"PDOName"},
{ &DEVPKEY_Device_Parent, L"Parent"},
{ &DEVPKEY_Device_DriverVersion, L"DriverVersion"},
{ &DEVPKEY_Device_LocationInfo, L"LocationInfo"},
{ &DEVPKEY_Device_FirmwareVersion, L"FirmwareVersion"},
{ &DEVPKEY_Device_Model, L"Model"},
{ &DEVPKEY_NAME, L"NAME"},
{ &DEVPKEY_Device_InstanceId, L"DeviceID"}
};
do
{
int n = RTL_NUMBER_OF(PropertyKeys);
do
{
CONST DEVPROPKEY *PropertyKey = PropertyKeys[--n].PropertyKey;
do
{
if (cb < rcb)
{
rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
}
status = CM_Get_DevNode_PropertyW(dnDevInst, PropertyKey, &PropertyType, pb, &rcb, 0);
if (status == CR_SUCCESS)
{
if (PropertyType == DEVPROP_TYPE_STRING)
{
DbgPrint("%s%S=[%S]\n", prefix, PropertyKeys[n].PropertyName, sz);
#ifdef OPEN_PDO
if (!n)
{
// DEVPKEY_Device_PDOName can use in NtOpenFile
RtlInitUnicodeString(&ObjectName, sz);
if (0 <= NtOpenFile(&hFile, FILE_READ_ATTRIBUTES|SYNCHRONIZE, &oa,
&iosb, FILE_SHARE_VALID_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT))
{
NtClose(hFile);
}
}
#endif
}
}
} while (status == CR_BUFFER_SMALL);
} while (n);
if (!*--prefix) break;
} while (CM_Get_Parent(&dnDevInst, dnDevInst, 0) == CR_SUCCESS);
}
also string returned by DEVPKEY_Device_PDOName we can use in NtOpenFile call for open PDO device.
My Env:
Qt 5.3.1
Windows 10
I need to find the path of mounted USB storage devices.
Through the path, I can copy the files via Qt.
I know there is a cross-platform libusb. But want to know any simple solution.
First you need to get removable drives:
void EnumUsbDrives() {
DWORD drv = ::GetLogicalDrives();
if (drv == 0) return;
DWORD mask = 1;
TCHAR szDrive[] = _TEXT("?:\\");
for (uint_t i = 0; i < ('Z' - 'A' + 1); i++, mask <<= 1) {
if (drv & mask) {
szDrive[0] = (TCHAR)(_T('A') + i);
if (::GetDriveType(szDrive) == DRIVE_REMOVABLE) {
bool bUSB = IsDriveUSB(szDrive);
if (bUSB) {
// Time do to something useful
}
}
}
}
}
Function IsDriveUSB is a bit more complicated. I have teared it from an in-house library; the function uses custom helper classes xregistry and xstring_nocase. Their purpose is pretty obvious, I believe you will have no trouble replacing it with other similar classes or API calls.
bool IsDriveUSB (LPCTSTR szDrive) throw() {
TCHAR szLogicalDrive[] = _TEXT("\\\\.\\x:");
szLogicalDrive[4] = szDrive[0];
HANDLE hDrive = ::CreateFile(szLogicalDrive, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (hDrive == INVALID_HANDLE_VALUE) return false; // Can't open drive so we have to assume the drive is fixed
VOLUME_DISK_EXTENTS vde;
DWORD dwBytesReturned = 0;
BOOL br = ::DeviceIoControl(hDrive, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, &vde, sizeof(vde), &dwBytesReturned, NULL);
::CloseHandle(hDrive);
if (!br) return false; // Can't get extents info so we have to assume the drive is fixed
if (vde.NumberOfDiskExtents != 1) return false;
ULONG uPhysDrive = vde.Extents[0].DiskNumber;
TCHAR szPhysDrive[16];
_stprintf(szPhysDrive, _TEXT("%u"), uPhysDrive);
try {
xregistry rk(HKEY_LOCAL_MACHINE, OS.Is64bit());
rk.open(_TEXT("SYSTEM\\CurrentControlSet\\services\\Disk\\Enum"), KEY_QUERY_VALUE);
if (!rk.value_exists(szPhysDrive)) return false;
xstring_nocase strInterface = rk.get_string(szPhysDrive).substring(0, 7);
return strInterface == _TEXT("USBSTOR");
}
catch (...) {
return false;
}
}
first we need enumerate all devices which support interface GUID_DEVINTERFACE_DISK. then we can open file on this interface and query for it STORAGE_ADAPTER_DESCRIPTOR or STORAGE_DEVICE_DESCRIPTOR and look for
BusType
Specifies a value of type STORAGE_BUS_TYPE that indicates the
type of the bus to which the device is connected.
for usb this will be BusTypeUsb
static volatile UCHAR guz;
CONFIGRET EnumUsbStor()
{
CONFIGRET err;
PVOID stack = alloca(guz);
ULONG BufferLen = 0, NeedLen = 256;
union {
PVOID buf;
PWSTR pszDeviceInterface;
};
for(;;)
{
if (BufferLen < NeedLen)
{
BufferLen = RtlPointerToOffset(buf = alloca((NeedLen - BufferLen) * sizeof(WCHAR)), stack) / sizeof(WCHAR);
}
switch (err = CM_Get_Device_Interface_ListW(const_cast<PGUID>(&GUID_DEVINTERFACE_DISK),
0, pszDeviceInterface, BufferLen, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
{
case CR_BUFFER_SMALL:
if (err = CM_Get_Device_Interface_List_SizeW(&NeedLen, const_cast<PGUID>(&GUID_DEVINTERFACE_DISK),
0, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
{
default:
return err;
}
continue;
case CR_SUCCESS:
while (*pszDeviceInterface)
{
BOOLEAN bIsUsb = FALSE;
HANDLE hFile = CreateFile(pszDeviceInterface, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
STORAGE_PROPERTY_QUERY spq = { StorageAdapterProperty, PropertyStandardQuery };
STORAGE_ADAPTER_DESCRIPTOR sad;
ULONG n;
if (DeviceIoControl(hFile, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), &sad, sizeof(sad), &n, 0))
{
bIsUsb = sad.BusType == BusTypeUsb;
}
CloseHandle(hFile);
}
pszDeviceInterface += 1 + wcslen(pszDeviceInterface);
}
return 0;
}
}
}
also we can look for EnumeratorName in interface string - are this is USBSTOR. fast end simply:
wcsstr(_wcsupr(pszDeviceInterface), L"\\USBSTOR#");
search for \USBSTOR# substring in interface name. or more correct - get Device_InstanceId from interface name and query it for DEVPKEY_Device_EnumeratorName
CONFIGRET IsUsbStor(DEVINST dnDevInst, BOOLEAN& bUsbStor)
{
ULONG cb = 0, rcb = 256;
PVOID stack = alloca(guz);
DEVPROPTYPE PropertyType;
CONFIGRET status;
union {
PVOID pv;
PWSTR EnumeratorName;
PBYTE pb;
};
do
{
if (cb < rcb)
{
rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
}
status = CM_Get_DevNode_PropertyW(dnDevInst, &DEVPKEY_Device_EnumeratorName, &PropertyType,
pb, &rcb, 0);
if (status == CR_SUCCESS)
{
if (PropertyType == DEVPROP_TYPE_STRING)
{
DbgPrint("EnumeratorName = %S\n", EnumeratorName);
bUsbStor = !_wcsicmp(L"USBSTOR", EnumeratorName);
}
else
{
status = CR_WRONG_TYPE;
}
break;
}
} while (status == CR_BUFFER_SMALL);
return status;
}
CONFIGRET IsUsbStor(PCWSTR pszDeviceInterface, BOOLEAN& bUsbStor)
{
ULONG cb = 0, rcb = 256;
PVOID stack = alloca(guz);
DEVPROPTYPE PropertyType;
CONFIGRET status;
union {
PVOID pv;
PWSTR DeviceID;
PBYTE pb;
};
do
{
if (cb < rcb)
{
rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
}
status = CM_Get_Device_Interface_PropertyW(pszDeviceInterface, &DEVPKEY_Device_InstanceId, &PropertyType, pb, &rcb, 0);
if (status == CR_SUCCESS)
{
if (PropertyType == DEVPROP_TYPE_STRING)
{
DbgPrint("DeviceID = %S\n", DeviceID);
DEVINST dnDevInst;
status = CM_Locate_DevNodeW(&dnDevInst, DeviceID, CM_LOCATE_DEVNODE_NORMAL);
if (status == CR_SUCCESS)
{
status = IsUsbStor(dnDevInst, bUsbStor);
}
}
else
{
status = CR_WRONG_TYPE;
}
break;
}
} while (status == CR_BUFFER_SMALL);
return status;
}
I'm trying to modify Windows access rights to a file in a way that only the owner (not even other Administrators) can access the file. Somewhat the equivalent of unix chmod 700 file.
I've played with denying rights to the general group (EVERYONE, ADMINISTRATORS) and granting them to the current user, but the current user always also loses the rights.
I tried to change the order (eas[0], eas[1]) and stuff, but without success.
Ideas anyone?
EXPLICIT_ACCESSA ea= { 0, }, eas[5]= { { 0, }, };
PACL pacl= 0;
ea.grfAccessPermissions = GENERIC_ALL;
ea.grfAccessMode = DENY_ACCESS ;
ea.grfInheritance = NO_INHERITANCE;
ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ea.Trustee.ptstrName = "EVERYONE";
eas[0]= ea;
ea.grfAccessPermissions = GENERIC_ALL;
ea.grfAccessMode = GRANT_ACCESS ;
ea.grfInheritance = NO_INHERITANCE;
ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
ea.Trustee.TrusteeType = TRUSTEE_IS_USER;
ea.Trustee.ptstrName = "CURRENT_USER";
eas[1]= ea;
rc= SetEntriesInAcl(2, &eas[0], NULL, &pacl);
rc= SetNamedSecurityInfoA((LPSTR)filename, SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION,
NULL, NULL, pacl, NULL);
In most cases, deny entries take precedent over allow entries. Since access is denied by default, you don't need the deny entry, however you will need to disable inherited permissions. You can do this by using the PROTECTED_DACL_SECURITY_INFORMATION flag.
#include <Windows.h>
#include <Aclapi.h>
#include <stdio.h>
int main(int argc, char ** argv)
{
EXPLICIT_ACCESS eas[1];
PACL pacl = 0;
DWORD rc;
eas[0].grfAccessPermissions = GENERIC_ALL;
eas[0].grfAccessMode = GRANT_ACCESS;
eas[0].grfInheritance = NO_INHERITANCE;
eas[0].Trustee.TrusteeForm = TRUSTEE_IS_NAME;
eas[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
eas[0].Trustee.ptstrName = L"CURRENT_USER";
rc = SetEntriesInAcl(1, &eas[0], NULL, &pacl);
if (rc != ERROR_SUCCESS)
{
printf("SetEntriesInAcl: %u\n", rc);
return 1;
}
rc = SetNamedSecurityInfo(L"C:\\working\\test.txt", SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
NULL, NULL, pacl, NULL);
if (rc != ERROR_SUCCESS)
{
printf("SetNamedSecurityInfo: %u\n", rc);
return 1;
}
printf("OK!\n");
return 0;
}
Note that an administrator can always reset the permissions in order to gain access to the file. If you really need to protect the data against other administrators you'll have to encrypt it. (And hope nobody installs a keylogger to steal your encryption password.)
While technically possible, it doesn't buy you much in the way of securing an object. A local administrator may not immediately be able to access a file, given a DACL with appropriate entries, but they can always take ownership of any object in the system. This grants them full control over the object, and they can manipulate its DACL and SACL.
formally you need next code
DWORD demo(PCWSTR filename)
{
EXPLICIT_ACCESS ea= {
GENERIC_ALL,
GRANT_ACCESS,
NO_INHERITANCE,
{ 0, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_NAME, TRUSTEE_IS_USER, L"CURRENT_USER"}
};
PACL pacl;
DWORD err = SetEntriesInAcl(1, &ea, NULL, &pacl);
if (!err)
{
err = ERROR_GEN_FAILURE;
if (pacl->AceCount == 1)
{
union {
PVOID pvAce;
PACE_HEADER Header;
PACCESS_ALLOWED_ACE pAce;
};
if (GetAce(pacl, 0, &pvAce) && Header->AceType == ACCESS_ALLOWED_ACE_TYPE)
{
HANDLE hFile = CreateFile(filename, WRITE_DAC|WRITE_OWNER, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
SECURITY_DESCRIPTOR sd;
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, TRUE, pacl, FALSE);
SetSecurityDescriptorOwner(&sd, &pAce->SidStart, FALSE);
SetSecurityDescriptorControl(&sd, SE_DACL_PROTECTED, SE_DACL_PROTECTED);
err = SetKernelObjectSecurity(hFile, DACL_SECURITY_INFORMATION|OWNER_SECURITY_INFORMATION, &sd)
? NOERROR : GetLastError();
CloseHandle(hFile);
}
else
{
err = GetLastError();
}
}
}
LocalFree(pacl);
}
return err;
}
note line
SetSecurityDescriptorControl(&sd, SE_DACL_PROTECTED, SE_DACL_PROTECTED);
with this code DACL for file will be have only one entry - GENERIC_ALL for current user. and all what explicitly not allowed in DACL - denied. but of course if user have SE_TAKE_OWNERSHIP_PRIVILEGE privilege - you can open file with WRITE_OWNER access and set self as owner. after this you can open file with WRITE_DAC and change DACL
as noted #Harry Johnston code can be and shorter if use SetNamedSecurityInfo
DWORD demo(PCWSTR filename)
{
EXPLICIT_ACCESS ea= {
GENERIC_ALL,
GRANT_ACCESS,
NO_INHERITANCE,
{ 0, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_NAME, TRUSTEE_IS_USER, L"CURRENT_USER"}
};
PACL pacl;
DWORD err = SetEntriesInAcl(1, &ea, NULL, &pacl);
if (!err)
{
err = ERROR_GEN_FAILURE;
if (pacl->AceCount == 1)
{
union {
PVOID pvAce;
PACE_HEADER Header;
PACCESS_ALLOWED_ACE pAce;
};
if (GetAce(pacl, 0, &pvAce) && Header->AceType == ACCESS_ALLOWED_ACE_TYPE)
{
err = SetNamedSecurityInfo((PWSTR)filename, SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION |PROTECTED_DACL_SECURITY_INFORMATION,
&pAce->SidStart, NULL, pacl, NULL);
}
}
LocalFree(pacl);
}
return err;
}