Winapi: Get the process which has specific handle of a file - c

Currently I have a software which has a file filter driver, during installation of software the driver is started as service in this way:
CreateService(serviceManager, name, displayName,
SERVICE_START | DELETE | SERVICE_QUERY_STATUS | SERVICE_STOP,
SERVICE_FILE_SYSTEM_DRIVER, SERVICE_AUTO_START, SERVICE_ERROR_IGNORE,
path, NULL, NULL, NULL, NULL, NULL);
Where path is C:\Program Files(x86)\TSU\driver\TSUfsd.sys.
The problem I'm having is during uninstallation of the software. It gives me access denied when software tries to delete TSUfsd.sys file.
I've checked how the software deletes the driver, and turns out it deletes it with DeleteService function, and waits for service to change its state from SERVICE_STOP_PENDING to SERVICE_STOPPED and if it doesn't happen after some time, it gets the service PID and kills it with ProcessTerminate and then tries to delete the file with rmdir /S /Q C:\Program Files(x86)\TSU\.
I've tried to find the process which could have had the handle of the file(with Process Explorer) but could not find any. Then I thought maybe the service is still alive so I typed sc query TSUfsd but the service was gone too.
I've also tried to change permissions and grant full permissions to my user but same error still occurred.
So my questions are:
Is there any other ways to check which process(or anything else) can have a hold of a file?
I've also noticed that, whenever I try to delete the file with Cygwin(rm TSUfsd.sys) it deletes the file without a problem. What is the difference between deleting file with cmd(del /f <filename>) and with cygwin?

For this task, starting with Windows Vista special exist FileProcessIdsUsingFileInformation FILE_INFORMATION_CLASS.
So we need open file with FILE_READ_ATTRIBUTES (this is possible even if somebody open file with 0 share mode. of if we have no access to file (by it DACL) but have read access to parent directory). and call NtQueryInformationFile with FileProcessIdsUsingFileInformation. on return we got FILE_PROCESS_IDS_USING_FILE_INFORMATION structure (defined in wdm.h) where list of ProcessId which hold this file(open file handle or mapped section. say if this file is exe/dll - we got process ids where it loaded). good also with this print process name for every id:
volatile UCHAR guz = 0;
NTSTATUS PrintProcessesUsingFile(HANDLE hFile)
{
NTSTATUS status;
IO_STATUS_BLOCK iosb;
ULONG cb = 0, rcb = FIELD_OFFSET(FILE_PROCESS_IDS_USING_FILE_INFORMATION, ProcessIdList[64]);
union {
PVOID buf;
PFILE_PROCESS_IDS_USING_FILE_INFORMATION ppiufi;
};
PVOID stack = alloca(guz);
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (0 <= (status = NtQueryInformationFile(hFile, &iosb, ppiufi, cb, FileProcessIdsUsingFileInformation)))
{
if (ppiufi->NumberOfProcessIdsInList)
{
PrintProcessesUsingFile(ppiufi);
}
}
rcb = (ULONG)iosb.Information;
} while (status == STATUS_INFO_LENGTH_MISMATCH);
return status;
}
NTSTATUS PrintProcessesUsingFile(POBJECT_ATTRIBUTES poa)
{
IO_STATUS_BLOCK iosb;
HANDLE hFile;
NTSTATUS status;
if (0 <= (status = NtOpenFile(&hFile, FILE_READ_ATTRIBUTES, poa, &iosb, FILE_SHARE_VALID_FLAGS, 0)))
{
status = PrintProcessesUsingFile(hFile);
NtClose(hFile);
}
return status;
}
NTSTATUS PrintProcessesUsingFile(PCWSTR FileName)
{
UNICODE_STRING ObjectName;
NTSTATUS status = RtlDosPathNameToNtPathName_U_WithStatus(FileName, &ObjectName, 0, 0);
if (0 <= status)
{
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName };
status = PrintProcessesUsingFile(&oa);
RtlFreeUnicodeString(&ObjectName);
}
return status;
}
NTSTATUS PrintProcessesUsingFile(PFILE_PROCESS_IDS_USING_FILE_INFORMATION ppiufi)
{
NTSTATUS status;
ULONG cb = 0x8000;
do
{
status = STATUS_INSUFFICIENT_RESOURCES;
if (PVOID buf = new BYTE[cb])
{
if (0 <= (status = NtQuerySystemInformation(SystemProcessInformation, buf, cb, &cb)))
{
union {
PVOID pv;
PBYTE pb;
PSYSTEM_PROCESS_INFORMATION pspi;
};
pv = buf;
ULONG NextEntryOffset = 0;
do
{
pb += NextEntryOffset;
ULONG NumberOfProcessIdsInList = ppiufi->NumberOfProcessIdsInList;
PULONG_PTR ProcessIdList = ppiufi->ProcessIdList;
do
{
if (*ProcessIdList++ == (ULONG_PTR)pspi->UniqueProcessId)
{
DbgPrint("%p %wZ\n", pspi->UniqueProcessId, &pspi->ImageName);
break;
}
} while (--NumberOfProcessIdsInList);
} while (NextEntryOffset = pspi->NextEntryOffset);
}
delete [] buf;
}
} while (status == STATUS_INFO_LENGTH_MISMATCH);
return status;
}

Related

CreateFile over USB HID device fails with Access Denied (5) since Windows 10 1809

Since the latest Windows 10 1809 update we can no longer open a USB HID keyboard-like device of ours using CreateFile. We reduced the problem to this minimal example:
#include <windows.h>
#include <setupapi.h>
#include <stdio.h>
#include <hidsdi.h>
void bad(const char *msg) {
DWORD w = GetLastError();
fprintf(stderr, "bad: %s, GetLastError() == 0x%08x\n", msg, (unsigned)w);
}
int main(void) {
int i;
GUID hidGuid;
HDEVINFO deviceInfoList;
const size_t DEVICE_DETAILS_SIZE = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + MAX_PATH;
SP_DEVICE_INTERFACE_DETAIL_DATA *deviceDetails = alloca(DEVICE_DETAILS_SIZE);
deviceDetails->cbSize = sizeof(*deviceDetails);
HidD_GetHidGuid(&hidGuid);
deviceInfoList = SetupDiGetClassDevs(&hidGuid, NULL, NULL,
DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
if(deviceInfoList == INVALID_HANDLE_VALUE) {
bad("SetupDiGetClassDevs");
return 1;
}
for (i = 0; ; ++i) {
SP_DEVICE_INTERFACE_DATA deviceInfo;
DWORD size = DEVICE_DETAILS_SIZE;
HIDD_ATTRIBUTES deviceAttributes;
HANDLE hDev = INVALID_HANDLE_VALUE;
fprintf(stderr, "Trying device %d\n", i);
deviceInfo.cbSize = sizeof(deviceInfo);
if (!SetupDiEnumDeviceInterfaces(deviceInfoList, 0, &hidGuid, i,
&deviceInfo)) {
if (GetLastError() == ERROR_NO_MORE_ITEMS) {
break;
} else {
bad("SetupDiEnumDeviceInterfaces");
continue;
}
}
if(!SetupDiGetDeviceInterfaceDetail(deviceInfoList, &deviceInfo,
deviceDetails, size, &size, NULL)) {
bad("SetupDiGetDeviceInterfaceDetail");
continue;
}
fprintf(stderr, "Opening device %s\n", deviceDetails->DevicePath);
hDev = CreateFile(deviceDetails->DevicePath, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, 0, NULL);
if(hDev == INVALID_HANDLE_VALUE) {
bad("CreateFile");
continue;
}
deviceAttributes.Size = sizeof(deviceAttributes);
if(HidD_GetAttributes(hDev, &deviceAttributes)) {
fprintf(stderr, "VID = %04x PID = %04x\n", (unsigned)deviceAttributes.VendorID, (unsigned)deviceAttributes.ProductID);
} else {
bad("HidD_GetAttributes");
}
CloseHandle(hDev);
}
SetupDiDestroyDeviceInfoList(deviceInfoList);
return 0;
}
It enumerates all HID devices, trying to obtain the vendor ID/product ID for each one using CreateFile over the path provided by SetupDiGetDeviceInterfaceDetail and then calling HidD_GetAttributes.
This code runs without problems on previous Windows versions (tested on Windows 7, Windows 10 1709 and 1803, and the original code from which this was extracted works since always from XP onwards), but with the latest update (1809) all keyboard devices (including ours) cannot be opened, as CreateFile fails with access denied (GetLastError() == 5). Running the program as administrator doesn't have any effect.
Comparing the output before and after the update, I noticed that the devices that now cannot be opened gained a trailing \kbd in the device path, i.e. what previously was
\\?\hid#vid_24d6&pid_8000&mi_00#7&294a3305&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
now is
\\?\hid#vid_24d6&pid_8000&mi_00#7&294a3305&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}\kbd
Is it a bug/new security restriction in the latest Windows 10 version? Was this code always wrong and it worked by chance before? Can this be fixed?
Update
As a desperate attempt, we tried to remove the \kbd from the returned string... and CreateFile now works! So, now we have a workaround, but it would be interesting to understand if that's a bug in SetupDiGetDeviceInterfaceDetail, if it's intentional and if this workaround is actually the correct thing to do.
I think this is a new security restriction in the latest Windows 10 version.
I looked for the string KBD (in UTF-16 format) - it exists only in two drivers in version 1809, hidclass.sys and kbdhid.sys, and doesn't exist in version 1709.
In hidclass.sys they changed the HidpRegisterDeviceInterface function. Before this release it called IoRegisterDeviceInterface with GUID_DEVINTERFACE_HID and the ReferenceString pointer set to 0. But in the new version, depending on the result of GetHidClassCollection, it passes KBD as ReferenceString pointer.
Inside kbdhid.sys they changed KbdHid_Create, and here is a check for the KBD string to return errors (access denied or sharing violation).
To understand more exactly why, more research is needed. Some disasm:
For reference, HidpRegisterDeviceInterface from 1709 build
here ReferenceString == 0 always (xor r8d,r8d), and there's no check cmp word [rbp + a],6 on class collection data
However, KbdHid_Create in 1809 contains a bug. The code is:
NTSTATUS KbdHid_Create(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
//...
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
if (PFILE_OBJECT FileObject = IrpSp->FileObject)
{
PCUNICODE_STRING FileName = &FileObject->FileName;
if (FileName->Length)
{
#if ver == 1809
UNICODE_STRING KBD = RTL_CONSTANT_STRING(L"KBD"); // !! bug !!
NTSTATUS status = RtlEqualUnicodeString(FileName, &KBD, FALSE)
? STATUS_SHARING_VIOLATION : STATUS_ACCESS_DENIED;
#else
NTSTATUS status = STATUS_ACCESS_DENIED;
#endif
// log
Irp->IoStatus.Status = status;
IofCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
}
// ...
}
What it this function trying to do here? It looks for passed PFILE_OBJECT FileObject from Irp current stack location. It no FileObject is provided or it has an empty name, allow open; otherwise, the open fails.
Before 1809 it always failed with error STATUS_ACCESS_DENIED (0xc0000022), but starting from 1809, the name is checked, and if it's equal to KBD (case sensitive) another error - STATUS_SHARING_VIOLATION is returned. However, name always begins with the \ symbol, so it will never match KBD. It can be \KBD, so, to fix this check, the following line needs to be changed to:
UNICODE_STRING KBD = RTL_CONSTANT_STRING(L"\\KBD");
and perform the comparison with this string. So, by design we should have got a STATUS_SHARING_VIOLATION error when trying to open a keyboard device via *\KBD name, but due to an implementation error we actually got STATUS_ACCESS_DENIED here
Another change was in HidpRegisterDeviceInterface - before the call to IoRegisterDeviceInterface on the device it queries the GetHidClassCollection result, and if some WORD (2 byte) field in the structure is equal to 6, adds KBD suffix (ReferenceString). I guess (but I'm not sure) that 6 can be the Usage ID for keyboard, and the rationale for this prefix is to set Exclusive access mode
Actually, we can have a FileName begin without \ if we use relative device open via OBJECT_ATTRIBUTES. So, just for test, we can do this: if the interface name ends with \KBD, first open the file without this suffix (so with empty relative device name), and this open must work ok; then, we can try relative open file with name KBD - we must get STATUS_SHARING_VIOLATION in 1809 and STATUS_ACCESS_DENIED in previous builds (but here we will be have no \KBD suffix):
void TestOpen(PWSTR pszDeviceInterface)
{
HANDLE hFile;
if (PWSTR c = wcsrchr(pszDeviceInterface, '\\'))
{
static const UNICODE_STRING KBD = RTL_CONSTANT_STRING(L"KBD");
if (!wcscmp(c + 1, KBD.Buffer))
{
*c = 0;
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, const_cast<PUNICODE_STRING>(&KBD) };
oa.RootDirectory = CreateFileW(pszDeviceInterface, 0,
FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (oa.RootDirectory != INVALID_HANDLE_VALUE)
{
IO_STATUS_BLOCK iosb;
// will be STATUS_SHARING_VIOLATION (c0000043)
NTSTATUS status = NtOpenFile(&hFile, SYNCHRONIZE, &oa, &iosb,
FILE_SHARE_VALID_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT);
CloseHandle(oa.RootDirectory);
if (0 <= status)
{
PrintAttr(hFile);
CloseHandle(hFile);
}
}
return ;
}
}
hFile = CreateFileW(pszDeviceInterface, 0,
FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
PrintAttr(hFile);
CloseHandle(hFile);
}
}
void PrintAttr(HANDLE hFile)
{
HIDD_ATTRIBUTES deviceAttributes = { sizeof(deviceAttributes) };
if(HidD_GetAttributes(hFile, &deviceAttributes)) {
printf("VID = %04x PID = %04x\r\n",
(ULONG)deviceAttributes.VendorID, (ULONG)deviceAttributes.ProductID);
} else {
bad(L"HidD_GetAttributes");
}
}
In a test on 1809 I actually got STATUS_SHARING_VIOLATION, that also shows another bug in kbdhid.KbdHid_Create - if we check FileName, we need to check RelatedFileObject - is it 0 or not.
Also, not related to bug, but as suggestion: it is more efficient to use CM_Get_Device_Interface_List instead of the SetupAPI:
volatile UCHAR guz = 0;
CONFIGRET EnumInterfaces(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)
{
TestOpen(pszDeviceInterface);
pszDeviceInterface += 1 + wcslen(pszDeviceInterface);
}
return 0;
}
}
}
EnumInterfaces(const_cast<PGUID>(&GUID_DEVINTERFACE_HID));
The fix is in this windows update released today (March 1, 2019).
https://support.microsoft.com/en-us/help/4482887/windows-10-update-kb4482887
You can find a workaround at Delphi-Praxis in German
For short: Change in the Unit JvHidControllerClass
if not HidD_GetAttributes(HidFileHandle, FAttributes) then
raise EControllerError.CreateRes(#RsEDeviceCannotBeIdentified);
to
HidD_GetAttributes(HidFileHandle, FAttributes);
and recompile the Delhi JCL and JCVL Components by running the JEDI Install EXE.

Reading Task Scheduler events from Event Log

I want to list all executed runs of custom task of the Task Scheduler in C/C++. Therefore I access the Event Log and try to extract the TaskScheduler log entries, like so (removed all error handling for simplicity):
HANDLE hEv = OpenEventLogA(NULL, "Microsoft-Windows-TaskScheduler/Operational");
DWORD nrRead = 0x10000, status = ERROR_SUCCESS, nrMin = 0, nrDone;
PBYTE buf = (PBYTE) malloc(nrRead);
while (status == ERROR_SUCCESS) {
if (!ReadEventLog(hEv, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ,
0, buf, nrRead, &nrDone, &nrMin)) status = GetLastError();
for (PBYTE pRec = buf, pEnd = buf + nrRead; pRec < pEnd;) {
(void) (pRec + sizeof(EVENTLOGRECORD)); // Store record
pRec += ((PEVENTLOGRECORD) pRec)->Length;
if (((PEVENTLOGRECORD) pRec)->Length == 0) break; // Avoid endless loop
}
}
Actually I am able to read events from the log (e.g. the WiFi log). But I cannot open the TaskScheduler log. It then does as described in the documentation and falls back to the Application log.
I tried different strings for the log's name:
Protocol name from the Event Log
Path to the protocol separated by slashes
English and localized names
None of it seems to work. So how can I open the TaskScheduler log? Is the log name localized and needs to be adjusted according to the current Operating System language? Is there another way to retrieve the TaskScheduler executions?
I have tried your code, seem like OpenEventLog can only open some log that frequently-used(not sure). However, there is another way to list TaskScheduler event:
use EvtSubscribe() to add the callback function, When a record is queried, print it out in XML format. Here is the code example:
#include <windows.h>
#include <sddl.h>
#include <stdio.h>
#include <winevt.h>
#pragma comment(lib, "wevtapi.lib")
const int SIZE_DATA = 4096;
TCHAR XMLDataCurrent[SIZE_DATA];
TCHAR XMLDataUser[SIZE_DATA];
#define ARRAY_SIZE 10
#define TIMEOUT 1000 // 1 second; Set and use in place of INFINITE in EvtNext call
DWORD PrintEvent(EVT_HANDLE hEvent); // Shown in the Rendering Events topic
DWORD WINAPI SubscriptionCallback(EVT_SUBSCRIBE_NOTIFY_ACTION action, PVOID pContext, EVT_HANDLE hEvent);
void main(void)
{
DWORD status = ERROR_SUCCESS;
EVT_HANDLE hResults = NULL;
//hResults = EvtQuery(NULL, pwsPath, pwsQuery, EvtQueryChannelPath );// EvtQueryReverseDirection);
hResults = EvtSubscribe(NULL, NULL, L"Microsoft-Windows-TaskScheduler/Operational", NULL, NULL, NULL, (EVT_SUBSCRIBE_CALLBACK)SubscriptionCallback, EvtSubscribeStartAtOldestRecord);
if (NULL == hResults)
{
status = GetLastError();
if (ERROR_EVT_CHANNEL_NOT_FOUND == status)
wprintf(L"The channel was not found.\n");
else if (ERROR_EVT_INVALID_QUERY == status)
// You can call the EvtGetExtendedStatus function to try to get
// additional information as to what is wrong with the query.
wprintf(L"The query is not valid.\n");
else
wprintf(L"EvtQuery failed with %lu.\n", status);
}
Sleep(1000);
cleanup:
if (hResults)
EvtClose(hResults);
}
// The callback that receives the events that match the query criteria.
DWORD WINAPI SubscriptionCallback(EVT_SUBSCRIBE_NOTIFY_ACTION action, PVOID pContext, EVT_HANDLE hEvent)
{
UNREFERENCED_PARAMETER(pContext);
DWORD status = ERROR_SUCCESS;
switch (action)
{
// You should only get the EvtSubscribeActionError action if your subscription flags
// includes EvtSubscribeStrict and the channel contains missing event records.
case EvtSubscribeActionError:
if (ERROR_EVT_QUERY_RESULT_STALE == (DWORD)hEvent)
{
wprintf(L"The subscription callback was notified that event records are missing.\n");
// Handle if this is an issue for your application.
}
else
{
wprintf(L"The subscription callback received the following Win32 error: %lu\n", (DWORD)hEvent);
}
break;
case EvtSubscribeActionDeliver:
if (ERROR_SUCCESS != (status = PrintEvent(hEvent)))
{
goto cleanup;
}
break;
default:
wprintf(L"SubscriptionCallback: Unknown action.\n");
}
cleanup:
if (ERROR_SUCCESS != status)
{
// End subscription - Use some kind of IPC mechanism to signal
// your application to close the subscription handle.
}
return status; // The service ignores the returned status.
}
DWORD PrintEvent(EVT_HANDLE hEvent)
{
DWORD status = ERROR_SUCCESS;
DWORD dwBufferSize = 0;
DWORD dwBufferUsed = 0;
DWORD dwPropertyCount = 0;
LPWSTR pRenderedContent = NULL;
if (!EvtRender(NULL, hEvent, EvtRenderEventXml, dwBufferSize, pRenderedContent, &dwBufferUsed, &dwPropertyCount))
{
if (ERROR_INSUFFICIENT_BUFFER == (status = GetLastError()))
{
dwBufferSize = dwBufferUsed;
pRenderedContent = (LPWSTR)malloc(dwBufferSize);
if (pRenderedContent)
{
EvtRender(NULL, hEvent, EvtRenderEventXml, dwBufferSize, pRenderedContent, &dwBufferUsed, &dwPropertyCount);
}
else
{
wprintf(L"malloc failed\n");
status = ERROR_OUTOFMEMORY;
goto cleanup;
}
}
if (ERROR_SUCCESS != (status = GetLastError()))
{
wprintf(L"EvtRender failed with %d\n", status);
goto cleanup;
}
}
ZeroMemory(XMLDataCurrent, SIZE_DATA);
lstrcpyW(XMLDataCurrent, pRenderedContent);
wprintf(L"EvtRender data %s\n", XMLDataCurrent);
cleanup:
if (pRenderedContent)
free(pRenderedContent);
return status;
}
Hope it could help you!
Thank you #zett42 for pointing me in the right direction and #Drake Wu for the detailed code example. But as I don't need any future event or asynchronous retrieval, I now implemented a simple synchronous function:
#define EVT_SIZE 10
int GetEvents(LPCWSTR query) {
DWORD xmlLen = 0;
LPCWSTR xml = NULL;
EVT_HANDLE hQuery = EvtQuery(NULL, NULL, query, EvtQueryChannelPath | EvtQueryTolerateQueryErrors));
while (true) {
EVT_HANDLE hEv[EVT_SIZE];
DWORD dwReturned = 0;
if (!EvtNext(hQuery, EVT_SIZE, hEv, INFINITE, 0, &dwReturned)) return 0;
// Loop over all events
for (DWORD i = 0; i < dwReturned; i++) {
DWORD nrRead = 0, nrProps = 0;
if (!EvtRender(NULL, hEv[i], EvtRenderEventXml, xmlLen, xml, &nrRead, &nrProps)) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
xmlLen = nrRead;
xml = (LPWSTR) realloc(xml, xmlLen);
if (xml) {
EvtRender(NULL, hEv[i], EvtRenderEventXml, xmlLen, xml, &nrRead, &nrProps);
} else {
return -1;
}
}
if (GetLastError() != ERROR_SUCCESS) return -1;
}
// Store event data
EvtClose(hEv[i]);
hEv[i] = NULL;
}
}
return 0;
}
Again I removed most error handling for simplifying the example. The Evt* functions indeed work for the retrieval of the TaskScheduler data (independently from the language).

Launching an APP from volume ID

I'm trying to launch an .exe file in windows using and a volume ID. The volume do not have a letter. Here is what I've tryed.
ShellExecute
When I call ShellExecute with the volume ID, Windows' explorer open the correct directory. Same thing if I try to open a folder inside the volume. However, if I try to open an exe file, nothing happen.
This is the way I call ShellExecute, which is part of windows.h :
char path[MAX_PATH] = R"(\\?\Volume{0dc7f9cc-d3ea-11e4-824b-806e6f6e6963}\App.exe)";
LONG_PTR returnValue = (LONG_PTR) ShellExecute(GetDesktopWindow(),NULL, path,NULL,NULL,SW_SHOW);
The error code returned is 2 :
The system cannot find the file specified.
CreateProcessW
After following the comments, I am now using . My new code look like this :
char path[MAX_PATH] = R"(\\?\Volume{0dc7f9cc-d3ea-11e4-824b-806e6f6e6963}\launcher.exe)";
STARTUPINFO info = {sizeof(info)};
PROCESS_INFORMATION processInfo;
if (CreateProcessW(path, NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &info, &processInfo))
{
WaitForSingleObject(processInfo.hProcess, INFINITE);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}
else {
DWORD dw = GetLastError();
printf("ERROR CODE: %d", dw);
}
But it still doesn't work. Error is the same (2).
Changing the path notation
From "\\?\Volume{ID}" to "\\.\Volume{ID}".
The strange thing is that I can list the drive's files or start a console process but I cannot launch any GUI EXE files. Error code remains 2.
Is the anythink I am missing here ? Any help are welcome. Thank you.
Answer
I've got confuse with the comments, using "\\.\Volume{ID}\myApp.exe" worked well if used with CreateProcess, not CreateProcessW.
There is the fully functional code (Don't forget to include windows.h).
char path[MAX_PATH] = R"(\\.\Volume{0dc7f9cc-d3ea-11e4-824b-806e6f6e6963}\App.exe)";
STARTUPINFO info = {sizeof(info)};
PROCESS_INFORMATION processInfo;
if (CreateProcess(path, NULL, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &info, &processInfo))
{
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}
else
{
DWORD dw = GetLastError();
printf("ERROR CODE WHILE STARTING: %d", dw);
}
problem in win32 subsystem - it not use native for windows NT Paths, but Win32 Paths which first must be converted to NT Paths before passing to kernel.
also some functions, like ShellExecuteEx or CreateProcessW accept not any valid win32 path, but only restricted subset - so called Drive Letter form. if pass path in form \\?\Volume{guid}\* - ShellExecuteEx and CreateProcessW always fail with this path (so called volume path) even if path correct (CreateFileW open this path). funny that CreateProcessW will be work with path \\.\Volume{guid}\* (if replace ? to . at [2] position) but ShellExecuteEx not worked with this path too.
only one reliable solution here - convert this volume form path to Drive Letter form. this can be done with help IOCTL_MOUNTMGR_QUERY_POINTS - need get list of all MOUNTMGR_MOUNT_POINT in system and do 2 loops by this list - first found device name by existing volume symlink. then in loop #2 - by already known device name found - dos-device name and got it drive letter
#include <mountmgr.h>
ULONG NtVolumePathToDosPath(PUNICODE_STRING VolumePath)
{
if (!MOUNTMGR_IS_NT_VOLUME_NAME(VolumePath))
{
return ERROR_PATH_NOT_FOUND;
}
static volatile UCHAR guz;
PVOID stack = alloca(guz);
ULONG cb = 0, rcb = 0x400;
union {
PVOID buf;
PMOUNTMGR_MOUNT_POINTS pmmp;
};
HANDLE hFile = CreateFile(MOUNTMGR_DOS_DEVICE_NAME, FILE_GENERIC_READ, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
{
return GetLastError();
}
static MOUNTMGR_MOUNT_POINT mmp;
ULONG dwError = NOERROR;
do
{
if (cb < rcb) cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
if (!DeviceIoControl(hFile, IOCTL_MOUNTMGR_QUERY_POINTS, &mmp, sizeof(mmp), buf, cb, &rcb, 0))
{
dwError = GetLastError();
rcb = pmmp->Size;
continue;
}
dwError = ERROR_PATH_NOT_FOUND;
if (ULONG NumberOfMountPoints = pmmp->NumberOfMountPoints)
{
PMOUNTMGR_MOUNT_POINT MountPoints = pmmp->MountPoints;
//loop #1: search for DeviceName linked to VolumePath
do
{
UNICODE_STRING SymbolicLinkName = {
MountPoints->SymbolicLinkNameLength,
SymbolicLinkName.Length,
(PWSTR)RtlOffsetToPointer(pmmp, MountPoints->SymbolicLinkNameOffset)
};
if (MOUNTMGR_IS_VOLUME_NAME(&SymbolicLinkName))
{
if (RtlEqualUnicodeString(&SymbolicLinkName, VolumePath, TRUE))
{
// found DeviceName
UNICODE_STRING _DeviceName = {
MountPoints->DeviceNameLength,
_DeviceName.Length,
(PWSTR)RtlOffsetToPointer(pmmp, MountPoints->DeviceNameOffset)
};
NumberOfMountPoints = pmmp->NumberOfMountPoints;
MountPoints = pmmp->MountPoints;
// loop #2: search for "drive letter" linked to DeviceName
do
{
UNICODE_STRING DeviceName = {
MountPoints->DeviceNameLength,
DeviceName.Length,
(PWSTR)RtlOffsetToPointer(pmmp, MountPoints->DeviceNameOffset)
};
if (RtlEqualUnicodeString(&_DeviceName, &DeviceName, FALSE))
{
SymbolicLinkName.MaximumLength = SymbolicLinkName.Length = MountPoints->SymbolicLinkNameLength;
SymbolicLinkName.Buffer = (PWSTR)RtlOffsetToPointer(pmmp, MountPoints->SymbolicLinkNameOffset);
if (MOUNTMGR_IS_DRIVE_LETTER(&SymbolicLinkName))
{
PWSTR szVolumePath = VolumePath->Buffer + 48;
*--szVolumePath = ':';
*--szVolumePath = SymbolicLinkName.Buffer[12];
*--szVolumePath = '\\';
*--szVolumePath = '?';
*--szVolumePath = '\\';
*--szVolumePath = '\\';
VolumePath->Buffer = szVolumePath;
dwError = NOERROR;
break;
}
}
} while (MountPoints++, --NumberOfMountPoints);
break;
}
}
} while (MountPoints++, --NumberOfMountPoints);
}
break;
} while (dwError == ERROR_MORE_DATA);
CloseHandle(hFile);
return dwError;
}
ULONG TestExecByVolumePath(PCWSTR szVolumePath)
{
size_t size = wcslen(szVolumePath) * sizeof(WCHAR);
if (size >= MAXUSHORT || size < 98)
{
return ERROR_PATH_NOT_FOUND;
}
UNICODE_STRING VolumePath;
VolumePath.Length = 96;
VolumePath.MaximumLength = (USHORT)size + sizeof(WCHAR);
memcpy(VolumePath.Buffer = (PWSTR)alloca(VolumePath.MaximumLength), szVolumePath, VolumePath.MaximumLength);
if (!MOUNTMGR_IS_DOS_VOLUME_NAME(&VolumePath))
{
return ERROR_PATH_NOT_FOUND;
}
VolumePath.Buffer[1] = '?';
ULONG dwErr = NtVolumePathToDosPath(&VolumePath);
if (dwErr == NOERROR)
{
SHELLEXECUTEINFOW sei = {sizeof(sei), 0, 0, L"open", VolumePath.Buffer, 0, 0, SW_SHOWDEFAULT };
if (!ShellExecuteExW(&sei))
{
dwErr = GetLastError();
}
}
return dwErr;
}
TestExecByVolumePath(L"\\\\?\\Volume{***}\\Windows\\System32\\notepad.exe");

Given I have PID and Process handle, can I write to stdin of existing process using C in WINAPI

I am trying to send a message to stdin of an existing process. This process writes to stdout, and therefore a command prompt is visible when the process is running.
I have looked here, but it is specific to .net. I would like to use C with the winapi.
Here is what I have tried:
I launch an exe that runs in a command prompt. When launched, the Process ID and Process Handle for the exe are captured by calling GetHandleOfProcessByExeName() (below). My understanding , through reading the MSDN page on WriteFile(), that I should be able to pass the handle of the process along with some text to the process pointed to by the handle. Say "prg.exe", is running on Windows 7. I get its process handle, then pass it along with a message to WriteToProcess().
I expect to see text appear on the command prompt, but this has not happened yet.
Relevant code:
int main(void)
{
HANDLE h = GetHandleOfProcessByExeName("prg.exe");
//this continually fails (returns FALSE)
BOOL status = WriteToProcess("test message", sizeof("test message"));
return 0;
}
HANDLE GetHandleOfProcessByExeName(char *exe)
{
PROCESSENTRY32 entry;
HANDLE hProcess=0;
entry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (Process32First(snapshot, &entry) == TRUE)
{
while (Process32Next(snapshot, &entry) == TRUE)
{
if (stricmp(entry.szExeFile, exe) == 0)
{
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);
CloseHandle(hProcess);
}
}
}
CloseHandle(snapshot);
return hProcess;
}
BOOL WriteToProcess(char *msg, size_t size)
{
BOOL status = FALSE;
status = WriteFile(gStdinWrite, msg, size, NULL, NULL);
return status; //TRUE for success
}
Edit to response to comments:
defined as file globals:
HANDLE gStdinRead = NULL;
HANDLE gStdinWrite = NULL;
int SystemX(char *cmd, int index)
{
STARTUPINFO sj;
PROCESS_INFORMATION pj;
SECURITY_ATTRIBUTES saAttr;
HANDLE h = 0;
int exit;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
ZeroMemory( &sj, sizeof(sj) );
sj.cb = sizeof(sj);
ZeroMemory( &pj, sizeof(pj) );
//create pipe and pass read end to CreateProcess
CreatePipe(&gStdinRead, &gStdinWrite, &saAttr, 0);
sj.hStdInput = gStdinRead;
sj.hStdOutput = gStdinWrite;
if(!CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &sj, &pj))
{
return h;
}
//Wait until child processes exit.
WaitForSingleObject( pj.hProcess, IGNORE ); //ingnore signal
//Get exit code
GetExitCodeProcess(pj.hProcess, (LPDWORD)(&exit));
return exit;
}

Decrypt WEP wlan profile key using CryptUnprotectData

I am trying to decrypt WEP profile's key using CryptUnprotectData. The way I fetched the profile key is by exporting the profile using netsh.
netsh wlan export profile name="MyWEP" folder="./"
For now, I manually copied the key material from the .xml file generated by the netsh command to my program. And the way, I am decrypting is -
DATA_BLOB DataOut, DataVerify;
DataOut.cbData = encryptData.length();
DataOut.pbData = (BYTE*)("I_Manually_Copy_The_WEP_Key_Here");
if (CryptUnprotectData( &DataOut,
NULL,
NULL,
NULL,
NULL,
0,
&DataVerify))
{
printf("The decrypted data is: %s\n", DataVerify.pbData);
}
else
{
printf("Failed. Error Code: %d", GetLastError());
}
But I am getting the error code 13 citing Invalid Data. What am I doing wrong ? On Win 7 and later, I can directly use WlanGetProfile with the parameter WLAN_PROFILE_GET_PLAINTEXT_KEY . But I have NO option on Vista than to use the CryptUnprotectData function. I have seen similar posts here, here but didn't get much useful information. Also, I am using the same system with same user log on credentials. Could any one please suggest me how to proceed ?
PS: I have posted the same question on Windows Desktop SDK forums, but haven't got response yet. Trying my luck on SO.
I like questions about Windows Security. So if I occasionally see such one I try to solve it.
In your case you did already the first step by the usage of netsh.exe wlan export profile ... to export the data from the WLAN profile in XML file. The file contains <keyMaterial> element. The data inside of the element are binary data encoded as the Hex: (something like 01000000D08C9DDF0115D1118C7A00C0...).
So what you need to do first of all is to decode the string to binary data. You can use CryptStringToBinary with CRYPT_STRING_HEX parameter to decode the string to binary.
The next step will be to fill DATA_BLOB with the binary data and call CryptUnprotectData to get the result, but... There are small problem. How you can read in the documentation of WlanGetProfile the following
By default, the keyMaterial element returned in the profile pointed to
by the pstrProfileXml is encrypted. If your process runs in the
context of the LocalSystem account on the same computer, then you can
unencrypt key material by calling the CryptUnprotectData function.
Windows Server 2008 and Windows Vista: The keyMaterial element
returned in the profile schema pointed to by the pstrProfileXml is
always encrypted. If your process runs in the context of the
LocalSystem account, then you can unencrypt key material by calling
the CryptUnprotectData function.
So to be able to unencrypt the key we have to call CryptUnprotectData in LocalSystem security context. If your program already run under LocalSystem context you can do this directly. If it's not so, but you have administrative rights or you have at least Debug privilege, you can "to borrow" the LocalSystem token from some other process running on the computer. For example one can get the process token of "winlogon.exe" process and impersonate it.
The following demo program enumerate processes using NtQuerySystemInformation method (see my old answer) which I personally prefer. One can use EnumProcesses or other well-known ways to do the same. Here is the code which worked at me
#include <Windows.h>
#include <tchar.h>
#include <stdio.h>
#pragma comment (lib, "Crypt32.lib")
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
typedef enum _SYSTEM_INFORMATION_CLASS {
SystemProcessInformation = 5
} SYSTEM_INFORMATION_CLASS;
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING;
typedef LONG KPRIORITY; // Thread priority
typedef struct _SYSTEM_PROCESS_INFORMATION_DETAILD {
ULONG NextEntryOffset;
ULONG NumberOfThreads;
LARGE_INTEGER SpareLi1;
LARGE_INTEGER SpareLi2;
LARGE_INTEGER SpareLi3;
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ImageName;
KPRIORITY BasePriority;
HANDLE UniqueProcessId;
ULONG InheritedFromUniqueProcessId;
ULONG HandleCount;
BYTE Reserved4[4];
PVOID Reserved5[11];
SIZE_T PeakPagefileUsage;
SIZE_T PrivatePageCount;
LARGE_INTEGER Reserved6[6];
} SYSTEM_PROCESS_INFORMATION_DETAILD, *PSYSTEM_PROCESS_INFORMATION_DETAILD;
typedef NTSTATUS (WINAPI *PFN_NT_QUERY_SYSTEM_INFORMATION)(
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT OPTIONAL PULONG ReturnLength
);
//
// The function changes a privilege named pszPrivilege for
// the current process. If bEnablePrivilege is FALSE, the privilege
// will be disabled, otherwise it will be enabled.
//
BOOL SetCurrentPrivilege (LPCTSTR pszPrivilege, // Privilege to enable/disable
BOOL bEnablePrivilege) // to enable or disable privilege
{
HANDLE hToken;
TOKEN_PRIVILEGES tp;
LUID luid;
TOKEN_PRIVILEGES tpPrevious;
DWORD cbPrevious = sizeof(TOKEN_PRIVILEGES);
BOOL bSuccess = FALSE;
if (!LookupPrivilegeValue(NULL, pszPrivilege, &luid)) return FALSE;
if (!OpenProcessToken (GetCurrentProcess(),
TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
&hToken
)) return FALSE;
//
// first pass. get current privilege setting
//
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = 0;
AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
&tpPrevious,
&cbPrevious);
if (GetLastError() == ERROR_SUCCESS) {
//
// second pass. set privilege based on previous setting
//
tpPrevious.PrivilegeCount = 1;
tpPrevious.Privileges[0].Luid = luid;
if(bEnablePrivilege)
tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED);
else
tpPrevious.Privileges[0].Attributes ^= (SE_PRIVILEGE_ENABLED &
tpPrevious.Privileges[0].Attributes);
AdjustTokenPrivileges(
hToken,
FALSE,
&tpPrevious,
cbPrevious,
NULL,
NULL);
if (GetLastError() == ERROR_SUCCESS) bSuccess=TRUE;
CloseHandle(hToken);
}
else {
DWORD dwErrorCode = GetLastError();
CloseHandle(hToken);
SetLastError(dwErrorCode);
}
return bSuccess;
}
DWORD GetProcessIdByProcessName (LPCWSTR pszProcessName)
{
SIZE_T bufferSize = 1024*sizeof(SYSTEM_PROCESS_INFORMATION_DETAILD);
PSYSTEM_PROCESS_INFORMATION_DETAILD pspid = NULL;
HANDLE hHeap = GetProcessHeap();
PBYTE pBuffer = NULL;
ULONG ReturnLength;
PFN_NT_QUERY_SYSTEM_INFORMATION pfnNtQuerySystemInformation = (PFN_NT_QUERY_SYSTEM_INFORMATION)
GetProcAddress (GetModuleHandle(TEXT("ntdll.dll")), "NtQuerySystemInformation");
NTSTATUS status;
int uLen = lstrlenW(pszProcessName)*sizeof(WCHAR);
__try {
pBuffer = (PBYTE) HeapAlloc (hHeap, 0, bufferSize);
#pragma warning(disable: 4127)
while (TRUE) {
#pragma warning(default: 4127)
status = pfnNtQuerySystemInformation (SystemProcessInformation, (PVOID)pBuffer,
bufferSize, &ReturnLength);
if (status == STATUS_SUCCESS)
break;
else if (status != STATUS_INFO_LENGTH_MISMATCH) { // 0xC0000004L
_tprintf (TEXT("ERROR 0x%X\n"), status);
return 1; // error
}
bufferSize *= 2;
pBuffer = (PBYTE) HeapReAlloc (hHeap, 0, (PVOID)pBuffer, bufferSize);
}
for (pspid = (PSYSTEM_PROCESS_INFORMATION_DETAILD)pBuffer; ;
pspid = (PSYSTEM_PROCESS_INFORMATION_DETAILD)(pspid->NextEntryOffset + (PBYTE)pspid)) {
if (pspid->ImageName.Length == uLen && lstrcmpiW(pspid->ImageName.Buffer, pszProcessName) == 0)
return (DWORD)pspid->UniqueProcessId;
if (pspid->NextEntryOffset == 0) break;
}
}
__finally {
pBuffer = (PBYTE) HeapFree (hHeap, 0, pBuffer);
}
return 0;
}
int _tmain()
{
BOOL bIsSuccess, bImpersonated = FALSE;
HANDLE hProcess = NULL, hProcessToken = NULL;
DATA_BLOB DataOut, DataVerify;
// !!! in the next line you should copy the string from <keyMaterial>
WCHAR szKey[] = L"01000000D08C9DDF0115D1118C7....";
BYTE byKey[1024];
DWORD cbBinary, dwFlags, dwSkip;
DWORD dwProcessId = GetProcessIdByProcessName(L"winlogon.exe");
if (dwProcessId == 0) return 1;
bIsSuccess = SetCurrentPrivilege(SE_DEBUG_NAME, TRUE);
if (!bIsSuccess) return GetLastError();
__try {
hProcess = OpenProcess(MAXIMUM_ALLOWED, FALSE, dwProcessId);
if (!hProcess) __leave;
bIsSuccess = OpenProcessToken (hProcess, MAXIMUM_ALLOWED, &hProcessToken);
if (!bIsSuccess) __leave;
bIsSuccess = ImpersonateLoggedOnUser(hProcessToken);
if (!bIsSuccess) __leave;
bImpersonated = TRUE;
cbBinary = sizeof(byKey);
bIsSuccess = CryptStringToBinary (szKey, lstrlenW(szKey), CRYPT_STRING_HEX, // CRYPT_STRING_HEX_ANY
byKey, &cbBinary, &dwSkip, &dwFlags);
if (!bIsSuccess) __leave;
DataOut.cbData = cbBinary;
DataOut.pbData = (BYTE*)byKey;
if (CryptUnprotectData (&DataOut, NULL, NULL, NULL, NULL, 0, &DataVerify)) {
_tprintf(TEXT("The decrypted data is: %hs\n"), DataVerify.pbData);
}
}
__finally {
if (bImpersonated)
RevertToSelf();
if (hProcess)
CloseHandle(hProcess);
if (hProcessToken)
CloseHandle(hProcessToken);
}
return 0;
}

Resources