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