]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - Ventoy2Disk/Ventoy2Disk/process.c
1. Optimization for WIMBOOT mode.
[Ventoy.git] / Ventoy2Disk / Ventoy2Disk / process.c
1 /*
2 * Rufus: The Reliable USB Formatting Utility
3 * Process search functionality
4 *
5 * Modified from Process Hacker:
6 * https://github.com/processhacker2/processhacker2/
7 * Copyright © 2017-2019 Pete Batard <pete@akeo.ie>
8 * Copyright © 2017 dmex
9 * Copyright © 2009-2016 wj32
10 * Copyright (c) 2020, longpanda <admin@ventoy.net>
11 *
12 * This program is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26
27 #include <Windows.h>
28 #include <winternl.h>
29 #include <commctrl.h>
30 #include <initguid.h>
31 #include <vds.h>
32 #include "resource.h"
33 #include "Language.h"
34 #include "Ventoy2Disk.h"
35 #include "fat_filelib.h"
36 #include "ff.h"
37 #include "process.h"
38 #include <Psapi.h>
39
40
41 OPENED_LIBRARIES_VARS;
42
43 STATIC WCHAR *_wHandleName = NULL;
44 static PVOID PhHeapHandle = NULL;
45
46
47 /*
48 * Convert an NT Status to an error message
49 *
50 * \param Status An operattonal status.
51 *
52 * \return An error message string.
53 *
54 */
55 char* NtStatusError(NTSTATUS Status) {
56 static char unknown[32];
57
58 switch (Status) {
59 case STATUS_SUCCESS:
60 return "Operation Successful";
61 case STATUS_UNSUCCESSFUL:
62 return "Operation Failed";
63 case STATUS_BUFFER_OVERFLOW:
64 return "Buffer Overflow";
65 case STATUS_NOT_IMPLEMENTED:
66 return "Not Implemented";
67 case STATUS_INFO_LENGTH_MISMATCH:
68 return "Info Length Mismatch";
69 case STATUS_INVALID_HANDLE:
70 return "Invalid Handle.";
71 case STATUS_INVALID_PARAMETER:
72 return "Invalid Parameter";
73 case STATUS_NO_MEMORY:
74 return "Not Enough Quota";
75 case STATUS_ACCESS_DENIED:
76 return "Access Denied";
77 case STATUS_BUFFER_TOO_SMALL:
78 return "Buffer Too Small";
79 case STATUS_OBJECT_TYPE_MISMATCH:
80 return "Wrong Type";
81 case STATUS_OBJECT_NAME_INVALID:
82 return "Object Name Invalid";
83 case STATUS_OBJECT_NAME_NOT_FOUND:
84 return "Object Name not found";
85 case STATUS_OBJECT_PATH_INVALID:
86 return "Object Path Invalid";
87 case STATUS_SHARING_VIOLATION:
88 return "Sharing Violation";
89 case STATUS_INSUFFICIENT_RESOURCES:
90 return "Insufficient resources";
91 case STATUS_NOT_SUPPORTED:
92 return "Operation is not supported";
93 default:
94 safe_sprintf(unknown, "Unknown error 0x%08lx", Status);
95 return unknown;
96 }
97 }
98
99
100 static NTSTATUS PhCreateHeap(VOID)
101 {
102 NTSTATUS status = STATUS_SUCCESS;
103
104 if (PhHeapHandle != NULL)
105 return STATUS_ALREADY_COMPLETE;
106
107 PF_INIT_OR_SET_STATUS(RtlCreateHeap, Ntdll);
108
109 if (NT_SUCCESS(status)) {
110 PhHeapHandle = pfRtlCreateHeap(HEAP_NO_SERIALIZE | HEAP_GROWABLE, NULL, 2 * MB, 1 * MB, NULL, NULL);
111 if (PhHeapHandle == NULL)
112 status = STATUS_UNSUCCESSFUL;
113 }
114
115 return status;
116 }
117
118 static NTSTATUS PhDestroyHeap(VOID)
119 {
120 NTSTATUS status = STATUS_SUCCESS;
121
122 if (PhHeapHandle == NULL)
123 return STATUS_ALREADY_COMPLETE;
124
125 PF_INIT_OR_SET_STATUS(RtlDestroyHeap, Ntdll);
126
127 if (NT_SUCCESS(status)) {
128 if (pfRtlDestroyHeap(PhHeapHandle) == NULL) {
129 PhHeapHandle = NULL;
130 }
131 else {
132 status = STATUS_UNSUCCESSFUL;
133 }
134 }
135
136 return status;
137 }
138
139 /**
140 * Allocates a block of memory.
141 *
142 * \param Size The number of bytes to allocate.
143 *
144 * \return A pointer to the allocated block of memory.
145 *
146 */
147 static PVOID PhAllocate(SIZE_T Size)
148 {
149 PF_INIT(RtlAllocateHeap, Ntdll);
150 if (pfRtlAllocateHeap == NULL)
151 return NULL;
152
153 return pfRtlAllocateHeap(PhHeapHandle, 0, Size);
154 }
155
156 /**
157 * Frees a block of memory allocated with PhAllocate().
158 *
159 * \param Memory A pointer to a block of memory.
160 *
161 */
162 static VOID PhFree(PVOID Memory)
163 {
164 PF_INIT(RtlFreeHeap, Ntdll);
165
166 if (pfRtlFreeHeap != NULL)
167 pfRtlFreeHeap(PhHeapHandle, 0, Memory);
168 }
169
170 /**
171 * Enumerates all open handles.
172 *
173 * \param Handles A variable which receives a pointer to a structure containing information about
174 * all opened handles. You must free the structure using PhFree() when you no longer need it.
175 *
176 * \return An NTStatus indicating success or the error code.
177 */
178 NTSTATUS PhEnumHandlesEx(PSYSTEM_HANDLE_INFORMATION_EX *Handles)
179 {
180 static ULONG initialBufferSize = 0x10000;
181 NTSTATUS status = STATUS_SUCCESS;
182 PVOID buffer;
183 ULONG bufferSize;
184
185 PF_INIT_OR_SET_STATUS(NtQuerySystemInformation, Ntdll);
186 if (!NT_SUCCESS(status))
187 return status;
188
189 bufferSize = initialBufferSize;
190 buffer = PhAllocate(bufferSize);
191 if (buffer == NULL)
192 return STATUS_NO_MEMORY;
193
194 while ((status = pfNtQuerySystemInformation(SystemExtendedHandleInformation,
195 buffer, bufferSize, NULL)) == STATUS_INFO_LENGTH_MISMATCH) {
196 PhFree(buffer);
197 bufferSize *= 2;
198
199 // Fail if we're resizing the buffer to something very large.
200 if (bufferSize > PH_LARGE_BUFFER_SIZE)
201 return STATUS_INSUFFICIENT_RESOURCES;
202
203 buffer = PhAllocate(bufferSize);
204 if (buffer == NULL)
205 return STATUS_NO_MEMORY;
206 }
207
208 if (!NT_SUCCESS(status)) {
209 PhFree(buffer);
210 return status;
211 }
212
213 if (bufferSize <= 0x200000)
214 initialBufferSize = bufferSize;
215 *Handles = (PSYSTEM_HANDLE_INFORMATION_EX)buffer;
216
217 return status;
218 }
219
220 /**
221 * Opens a process.
222 *
223 * \param ProcessHandle A variable which receives a handle to the process.
224 * \param DesiredAccess The desired access to the process.
225 * \param ProcessId The ID of the process.
226 *
227 * \return An NTStatus indicating success or the error code.
228 */
229 NTSTATUS PhOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, HANDLE ProcessId)
230 {
231 NTSTATUS status = STATUS_SUCCESS;
232 OBJECT_ATTRIBUTES objectAttributes;
233 CLIENT_ID clientId;
234
235 if ((LONG_PTR)ProcessId == (LONG_PTR)GetCurrentProcessId()) {
236 *ProcessHandle = NtCurrentProcess();
237 return 0;
238 }
239
240 PF_INIT_OR_SET_STATUS(NtOpenProcess, Ntdll);
241 if (!NT_SUCCESS(status))
242 return status;
243
244 clientId.UniqueProcess = ProcessId;
245 clientId.UniqueThread = NULL;
246
247 InitializeObjectAttributes(&objectAttributes, NULL, 0, NULL, NULL);
248 status = pfNtOpenProcess(ProcessHandle, DesiredAccess, &objectAttributes, &clientId);
249
250 return status;
251 }
252
253 /**
254 * Query processes with open handles to a file, volume or disk.
255 *
256 * \param VolumeOrFileHandle The handle to the target.
257 * \param Information The returned list of processes.
258 *
259 * \return An NTStatus indicating success or the error code.
260 */
261 NTSTATUS PhQueryProcessesUsingVolumeOrFile(HANDLE VolumeOrFileHandle,
262 PFILE_PROCESS_IDS_USING_FILE_INFORMATION *Information)
263 {
264 static ULONG initialBufferSize = 16 * KB;
265 NTSTATUS status = STATUS_SUCCESS;
266 PVOID buffer;
267 ULONG bufferSize;
268 IO_STATUS_BLOCK isb;
269
270 PF_INIT_OR_SET_STATUS(NtQueryInformationFile, NtDll);
271 if (!NT_SUCCESS(status))
272 return status;
273
274 bufferSize = initialBufferSize;
275 buffer = PhAllocate(bufferSize);
276 if (buffer == NULL)
277 return STATUS_INSUFFICIENT_RESOURCES;
278
279 while ((status = pfNtQueryInformationFile(VolumeOrFileHandle, &isb, buffer, bufferSize,
280 FileProcessIdsUsingFileInformation)) == STATUS_INFO_LENGTH_MISMATCH) {
281 PhFree(buffer);
282 bufferSize *= 2;
283 // Fail if we're resizing the buffer to something very large.
284 if (bufferSize > 64 * MB)
285 return STATUS_INSUFFICIENT_RESOURCES;
286 buffer = PhAllocate(bufferSize);
287 }
288
289 if (!NT_SUCCESS(status)) {
290 PhFree(buffer);
291 return status;
292 }
293
294 if (bufferSize <= 64 * MB)
295 initialBufferSize = bufferSize;
296 *Information = (PFILE_PROCESS_IDS_USING_FILE_INFORMATION)buffer;
297
298 return status;
299 }
300
301 /**
302 * Query the full commandline that was used to create a process.
303 * This can be helpful to differentiate between service instances (svchost.exe).
304 * Taken from: https://stackoverflow.com/a/14012919/1069307
305 *
306 * \param hProcess A handle to a process.
307 *
308 * \return A Unicode commandline string, or NULL on error.
309 * The returned string must be freed by the caller.
310 */
311 static PWSTR GetProcessCommandLine(HANDLE hProcess)
312 {
313 PWSTR wcmdline = NULL;
314 BOOL wow;
315 DWORD pp_offset, cmd_offset;
316 NTSTATUS status = STATUS_SUCCESS;
317 SYSTEM_INFO si;
318 PBYTE peb = NULL, pp = NULL;
319
320 // Determine if 64 or 32-bit processor
321 GetNativeSystemInfo(&si);
322 if ((si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) || (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64)) {
323 pp_offset = 0x20;
324 cmd_offset = 0x70;
325 }
326 else {
327 pp_offset = 0x10;
328 cmd_offset = 0x40;
329 }
330
331 // PEB and Process Parameters (we only need the beginning of these structs)
332 peb = (PBYTE)calloc(pp_offset + 8, 1);
333 if (peb == NULL)
334 goto out;
335 pp = (PBYTE)calloc(cmd_offset + 16, 1);
336 if (pp == NULL)
337 goto out;
338
339 IsWow64Process(GetCurrentProcess(), &wow);
340 if (wow) {
341 // 32-bit process running on a 64-bit OS
342 PROCESS_BASIC_INFORMATION_WOW64 pbi = { 0 };
343 ULONGLONG params;
344 UNICODE_STRING_WOW64* ucmdline;
345
346 PF_INIT_OR_OUT(NtWow64QueryInformationProcess64, NtDll);
347 PF_INIT_OR_OUT(NtWow64ReadVirtualMemory64, NtDll);
348
349 status = pfNtWow64QueryInformationProcess64(hProcess, 0, &pbi, sizeof(pbi), NULL);
350 if (!NT_SUCCESS(status))
351 goto out;
352
353 status = pfNtWow64ReadVirtualMemory64(hProcess, pbi.PebBaseAddress, peb, pp_offset + 8, NULL);
354 if (!NT_SUCCESS(status))
355 goto out;
356
357 // Read Process Parameters from the 64-bit address space
358 params = (ULONGLONG)*((ULONGLONG*)(peb + pp_offset));
359 status = pfNtWow64ReadVirtualMemory64(hProcess, params, pp, cmd_offset + 16, NULL);
360 if (!NT_SUCCESS(status))
361 goto out;
362
363 ucmdline = (UNICODE_STRING_WOW64*)(pp + cmd_offset);
364 wcmdline = (PWSTR)calloc(ucmdline->Length + 1, sizeof(WCHAR));
365 if (wcmdline == NULL)
366 goto out;
367 status = pfNtWow64ReadVirtualMemory64(hProcess, ucmdline->Buffer, wcmdline, ucmdline->Length, NULL);
368 if (!NT_SUCCESS(status)) {
369 safe_free(wcmdline);
370 goto out;
371 }
372 }
373 else {
374 // 32-bit process on a 32-bit OS, or 64-bit process on a 64-bit OS
375 PROCESS_BASIC_INFORMATION pbi = { 0 };
376 PBYTE* params;
377 UNICODE_STRING* ucmdline;
378
379 PF_INIT_OR_OUT(NtQueryInformationProcess, NtDll);
380
381 status = pfNtQueryInformationProcess(hProcess, 0, &pbi, sizeof(pbi), NULL);
382 if (!NT_SUCCESS(status))
383 goto out;
384
385 // Read PEB
386 if (!ReadProcessMemory(hProcess, pbi.PebBaseAddress, peb, pp_offset + 8, NULL))
387 goto out;
388
389 // Read Process Parameters
390 params = (PBYTE*)*(LPVOID*)(peb + pp_offset);
391 if (!ReadProcessMemory(hProcess, params, pp, cmd_offset + 16, NULL))
392 goto out;
393
394 ucmdline = (UNICODE_STRING*)(pp + cmd_offset);
395 // In the absolute, someone could craft a process with dodgy attributes to try to cause an overflow
396 ucmdline->Length = min(ucmdline->Length, 512);
397 wcmdline = (PWSTR)calloc(ucmdline->Length + 1, sizeof(WCHAR));
398 if (!ReadProcessMemory(hProcess, ucmdline->Buffer, wcmdline, ucmdline->Length, NULL)) {
399 safe_free(wcmdline);
400 goto out;
401 }
402 }
403
404 out:
405 free(peb);
406 free(pp);
407 return wcmdline;
408 }
409
410
411 static int GetDevicePathName(PHY_DRIVE_INFO *pPhyDrive, WCHAR *wDevPath)
412 {
413 int i;
414 CHAR PhyDrive[128];
415 CHAR DevPath[MAX_PATH] = { 0 };
416
417 safe_sprintf(PhyDrive, "\\\\.\\PhysicalDrive%d", pPhyDrive->PhyDrive);
418
419 if (0 == QueryDosDeviceA(PhyDrive + 4, DevPath, sizeof(DevPath)))
420 {
421 Log("QueryDosDeviceA failed error:%u", GetLastError());
422 strcpy_s(DevPath, sizeof(DevPath), "???");
423 }
424 else
425 {
426 Log("QueryDosDeviceA success %s", DevPath);
427 }
428
429 for (i = 0; DevPath[i] && i < MAX_PATH; i++)
430 {
431 wDevPath[i] = DevPath[i];
432 }
433
434 return 0;
435 }
436
437
438 static __inline DWORD GetModuleFileNameExU(HANDLE hProcess, HMODULE hModule, char* lpFilename, DWORD nSize)
439 {
440 DWORD ret = 0, err = ERROR_INVALID_DATA;
441 // coverity[returned_null]
442 walloc(lpFilename, nSize);
443 ret = GetModuleFileNameExW(hProcess, hModule, wlpFilename, nSize);
444 err = GetLastError();
445 if ((ret != 0)
446 && ((ret = wchar_to_utf8_no_alloc(wlpFilename, lpFilename, nSize)) == 0)) {
447 err = GetLastError();
448 }
449 wfree(lpFilename);
450 SetLastError(err);
451 return ret;
452 }
453
454 int FindProcessOccupyDisk(HANDLE hDrive, PHY_DRIVE_INFO *pPhyDrive)
455 {
456 WCHAR wDevPath[MAX_PATH] = { 0 };
457 const char *access_rights_str[8] = { "n", "r", "w", "rw", "x", "rx", "wx", "rwx" };
458 NTSTATUS status = STATUS_SUCCESS;
459 PSYSTEM_HANDLE_INFORMATION_EX handles = NULL;
460 POBJECT_NAME_INFORMATION buffer = NULL;
461 ULONG_PTR i;
462 ULONG_PTR pid[2];
463 ULONG_PTR last_access_denied_pid = 0;
464 ULONG bufferSize;
465 USHORT wHandleNameLen;
466 HANDLE dupHandle = NULL;
467 HANDLE processHandle = NULL;
468 BOOLEAN bFound = FALSE, bGotCmdLine, verbose = TRUE;
469 ULONG access_rights = 0;
470 DWORD size;
471 char cmdline[MAX_PATH] = { 0 };
472 wchar_t wexe_path[MAX_PATH], *wcmdline;
473 int cur_pid;
474
475
476 Log("FindProcessOccupyDisk for PhyDrive %d", pPhyDrive->PhyDrive);
477
478 GetDevicePathName(pPhyDrive, wDevPath);
479 _wHandleName = wDevPath;
480
481
482 PF_INIT_OR_SET_STATUS(NtQueryObject, Ntdll);
483 PF_INIT_OR_SET_STATUS(NtDuplicateObject, NtDll);
484 PF_INIT_OR_SET_STATUS(NtClose, NtDll);
485
486 if (NT_SUCCESS(status))
487 status = PhCreateHeap();
488
489 if (NT_SUCCESS(status))
490 status = PhEnumHandlesEx(&handles);
491
492 if (!NT_SUCCESS(status)) {
493 Log("Warning: Could not enumerate process handles: %s", NtStatusError(status));
494 goto out;
495 }
496
497 pid[0] = (ULONG_PTR)0;
498 cur_pid = 1;
499
500 wHandleNameLen = (USHORT)wcslen(_wHandleName);
501
502 bufferSize = 0x200;
503 buffer = PhAllocate(bufferSize);
504 if (buffer == NULL)
505 goto out;
506
507 for (i = 0;; i++) {
508 ULONG attempts = 8;
509 PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handleInfo =
510 (i < handles->NumberOfHandles) ? &handles->Handles[i] : NULL;
511
512 if ((dupHandle != NULL) && (processHandle != NtCurrentProcess())) {
513 pfNtClose(dupHandle);
514 dupHandle = NULL;
515 }
516
517 // Update the current handle's process PID and compare against last
518 // Note: Be careful about not trying to overflow our list!
519 pid[cur_pid] = (handleInfo != NULL) ? handleInfo->UniqueProcessId : -1;
520
521 if (pid[0] != pid[1]) {
522 cur_pid = (cur_pid + 1) % 2;
523
524 // If we're switching process and found a match, print it
525 if (bFound) {
526 Log("* [%06u] %s (%s)", (UINT32)pid[cur_pid], cmdline, access_rights_str[access_rights & 0x7]);
527 bFound = FALSE;
528 access_rights = 0;
529 }
530
531 // Close the previous handle
532 if (processHandle != NULL) {
533 if (processHandle != NtCurrentProcess())
534 pfNtClose(processHandle);
535 processHandle = NULL;
536 }
537 }
538
539 // Exit loop condition
540 if (i >= handles->NumberOfHandles)
541 break;
542
543 // Don't bother with processes we can't access
544 if (handleInfo->UniqueProcessId == last_access_denied_pid)
545 continue;
546
547 // Filter out handles that aren't opened with Read (bit 0), Write (bit 1) or Execute (bit 5) access
548 if ((handleInfo->GrantedAccess & 0x23) == 0)
549 continue;
550
551 // Open the process to which the handle we are after belongs, if not already opened
552 if (pid[0] != pid[1]) {
553 status = PhOpenProcess(&processHandle, PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
554 (HANDLE)handleInfo->UniqueProcessId);
555 // There exists some processes we can't access
556 if (!NT_SUCCESS(status)) {
557 //Log("SearchProcess: Could not open process %ld: %s",
558 // handleInfo->UniqueProcessId, NtStatusError(status));
559 processHandle = NULL;
560 if (status == STATUS_ACCESS_DENIED) {
561 last_access_denied_pid = handleInfo->UniqueProcessId;
562 }
563 continue;
564 }
565 }
566
567 // Now duplicate this handle onto our own process, so that we can access its properties
568 if (processHandle == NtCurrentProcess()) {
569 continue;
570 }
571 else {
572 status = pfNtDuplicateObject(processHandle, (HANDLE)handleInfo->HandleValue,
573 NtCurrentProcess(), &dupHandle, 0, 0, 0);
574 if (!NT_SUCCESS(status))
575 continue;
576 }
577
578 // Filter non-storage handles. We're not interested in them and they make NtQueryObject() freeze
579 if (GetFileType(dupHandle) != FILE_TYPE_DISK)
580 continue;
581
582 // A loop is needed because the I/O subsystem likes to give us the wrong return lengths...
583 do {
584 ULONG returnSize;
585 // TODO: We might potentially still need a timeout on ObjectName queries, as PH does...
586 status = pfNtQueryObject(dupHandle, ObjectNameInformation, buffer, bufferSize, &returnSize);
587 if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_INFO_LENGTH_MISMATCH ||
588 status == STATUS_BUFFER_TOO_SMALL) {
589 Log("SearchProcess: Realloc from %d to %d", bufferSize, returnSize);
590 bufferSize = returnSize;
591 PhFree(buffer);
592 buffer = PhAllocate(bufferSize);
593 }
594 else {
595 break;
596 }
597 } while (--attempts);
598 if (!NT_SUCCESS(status)) {
599 Log("SearchProcess: NtQueryObject failed for handle %X of process %ld: %s",
600 handleInfo->HandleValue, handleInfo->UniqueProcessId, NtStatusError(status));
601 continue;
602 }
603
604 // we are looking for a partial match and the current length is smaller
605 if (wHandleNameLen > buffer->Name.Length)
606 continue;
607
608 // Match against our target string
609 if (wcsncmp(_wHandleName, buffer->Name.Buffer, wHandleNameLen) != 0)
610 continue;
611
612 // If we are here, we have a process accessing our target!
613 bFound = TRUE;
614
615 // Keep a mask of all the access rights being used
616 access_rights |= handleInfo->GrantedAccess;
617 // The Executable bit is in a place we don't like => reposition it
618 if (access_rights & 0x20)
619 access_rights = (access_rights & 0x03) | 0x04;
620
621 // If this is the very first process we find, print a header
622 if (cmdline[0] == 0)
623 Log("WARNING: The following process(es) or service(s) are accessing %S:", _wHandleName);
624
625 // Where possible, try to get the full command line
626 bGotCmdLine = FALSE;
627 size = MAX_PATH;
628 wcmdline = GetProcessCommandLine(processHandle);
629 if (wcmdline != NULL) {
630 bGotCmdLine = TRUE;
631 wchar_to_utf8_no_alloc(wcmdline, cmdline, sizeof(cmdline));
632 free(wcmdline);
633 }
634
635 // If we couldn't get the full commandline, try to get the executable path
636 if (!bGotCmdLine)
637 bGotCmdLine = (GetModuleFileNameExU(processHandle, 0, cmdline, MAX_PATH - 1) != 0);
638
639 // The above may not work on Windows 7, so try QueryFullProcessImageName (Vista or later)
640 if (!bGotCmdLine) {
641 bGotCmdLine = QueryFullProcessImageNameW(processHandle, 0, wexe_path, &size);
642 if (bGotCmdLine)
643 wchar_to_utf8_no_alloc(wexe_path, cmdline, sizeof(cmdline));
644 }
645
646 // Still nothing? Try GetProcessImageFileName. Note that GetProcessImageFileName uses
647 // '\Device\Harddisk#\Partition#\' instead drive letters
648 if (!bGotCmdLine) {
649 bGotCmdLine = (GetProcessImageFileNameW(processHandle, wexe_path, MAX_PATH) != 0);
650 if (bGotCmdLine)
651 wchar_to_utf8_no_alloc(wexe_path, cmdline, sizeof(cmdline));
652 }
653
654 // Complete failure => Just craft a default process name that includes the PID
655 if (!bGotCmdLine) {
656 safe_sprintf(cmdline, "Unknown_Process_0x%llx", (ULONGLONG)handleInfo->UniqueProcessId);
657 }
658 }
659
660 out:
661 if (cmdline[0] != 0)
662 Log("You should close these applications before attempting to reformat the drive.");
663 else
664 Log("NOTE: Could not identify the process(es) or service(s) accessing %S", _wHandleName);
665
666 PhFree(buffer);
667 PhFree(handles);
668 PhDestroyHeap();
669
670 return 0;
671 }
672