diff --git a/src/process.c b/src/process.c index 5159adee..c55bbe04 100644 --- a/src/process.c +++ b/src/process.c @@ -41,6 +41,9 @@ PF_TYPE_DECL(NTAPI, BOOLEAN, RtlFreeHeap, (PVOID, ULONG, PVOID)); PF_TYPE_DECL(NTAPI, NTSTATUS, NtQuerySystemInformation, (SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG)); PF_TYPE_DECL(NTAPI, NTSTATUS, NtQueryInformationFile, (HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS)); +PF_TYPE_DECL(NTAPI, NTSTATUS, NtQueryInformationProcess, (HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG)); +PF_TYPE_DECL(NTAPI, NTSTATUS, NtWow64QueryInformationProcess64, (HANDLE, ULONG, PVOID, ULONG, PULONG)); +PF_TYPE_DECL(NTAPI, NTSTATUS, NtWow64ReadVirtualMemory64, (HANDLE, PVOID64, PVOID, ULONG64, PULONG64)); PF_TYPE_DECL(NTAPI, NTSTATUS, NtQueryObject, (HANDLE, OBJECT_INFORMATION_CLASS, PVOID, ULONG, PULONG)); PF_TYPE_DECL(NTAPI, NTSTATUS, NtDuplicateObject, (HANDLE, HANDLE, HANDLE, PHANDLE, ACCESS_MASK, ULONG, ULONG)); PF_TYPE_DECL(NTAPI, NTSTATUS, NtOpenProcess, (PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, CLIENT_ID*)); @@ -263,13 +266,13 @@ NTSTATUS PhOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, HANDLE } /** -* Query processes with open handles to a file, volume or disk. -* -* \param VolumeOrFileHandle The handle to the target. -* \param Information The returned list of processes. -* -* \return An NTStatus indicating success or the error code. -*/ + * Query processes with open handles to a file, volume or disk. + * + * \param VolumeOrFileHandle The handle to the target. + * \param Information The returned list of processes. + * + * \return An NTStatus indicating success or the error code. + */ NTSTATUS PhQueryProcessesUsingVolumeOrFile(HANDLE VolumeOrFileHandle, PFILE_PROCESS_IDS_USING_FILE_INFORMATION *Information) { @@ -310,6 +313,110 @@ NTSTATUS PhQueryProcessesUsingVolumeOrFile(HANDLE VolumeOrFileHandle, return status; } +/** + * Query the full commandline that was used to create a process. + * This can be helpful to differentiate between service instances (svchost.exe). + * Taken from: https://stackoverflow.com/a/14012919/1069307 + * + * \param hProcess A handle to a process. + * + * \return A Unicode commandline string, or NULL on error. + * The returned string must be freed by the caller. + */ +static PWSTR GetProcessCommandLine(HANDLE hProcess) +{ + PWSTR wcmdline = NULL; + BOOL wow; + DWORD pp_offset, cmd_offset; + NTSTATUS status = STATUS_SUCCESS; + SYSTEM_INFO si; + PBYTE peb = NULL, pp = NULL; + + // Determine if 64 or 32-bit processor + GetNativeSystemInfo(&si); + if ((si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) || (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64)) { + pp_offset = 0x20; + cmd_offset = 0x70; + } else { + pp_offset = 0x10; + cmd_offset = 0x40; + } + + // PEB and Process Parameters (we only need the beginning of these structs) + peb = (PBYTE)calloc(pp_offset + 8, 1); + if (peb == NULL) + goto out; + pp = (PBYTE)calloc(cmd_offset + 16, 1); + if (pp == NULL) + goto out; + + IsWow64Process(GetCurrentProcess(), &wow); + if (wow) { + // 32-bit process running on a 64-bit OS + PROCESS_BASIC_INFORMATION_WOW64 pbi = { 0 }; + PVOID64 params; + UNICODE_STRING_WOW64* ucmdline; + + PF_INIT_OR_OUT(NtWow64QueryInformationProcess64, NtDll); + PF_INIT_OR_OUT(NtWow64ReadVirtualMemory64, NtDll); + + status = pfNtWow64QueryInformationProcess64(hProcess, 0, &pbi, sizeof(pbi), NULL); + if (!NT_SUCCESS (status)) + goto out; + + status = pfNtWow64ReadVirtualMemory64(hProcess, pbi.PebBaseAddress, peb, pp_offset + 8, NULL); + if (!NT_SUCCESS (status)) + goto out; + + // Read Process Parameters from the 64-bit address space + params = (PVOID64) *((PVOID64*)(peb + pp_offset)); + status = pfNtWow64ReadVirtualMemory64(hProcess, params, pp, cmd_offset + 16, NULL); + if (!NT_SUCCESS (status)) + goto out; + + ucmdline = (UNICODE_STRING_WOW64*)(pp + cmd_offset); + wcmdline = (PWSTR)calloc(ucmdline->Length + 1, sizeof(WCHAR)); + if (wcmdline == NULL) + goto out; + status = pfNtWow64ReadVirtualMemory64(hProcess, ucmdline->Buffer, wcmdline, ucmdline->Length, NULL); + if (!NT_SUCCESS (status)) { + safe_free(wcmdline); + goto out; + } + } else { + // 32-bit process on a 32-bit OS, or 64-bit process on a 64-bit OS + PROCESS_BASIC_INFORMATION pbi = { 0 }; + PBYTE* params; + UNICODE_STRING* ucmdline; + + PF_INIT_OR_OUT(NtQueryInformationProcess, NtDll); + + status = pfNtQueryInformationProcess(hProcess, 0, &pbi, sizeof(pbi), NULL); + if (!NT_SUCCESS (status)) + goto out; + + // Read PEB + if (!ReadProcessMemory(hProcess, pbi.PebBaseAddress, peb, pp_offset + 8, NULL)) + goto out; + + // Read Process Parameters + params = (PBYTE*)*(LPVOID*)(peb + pp_offset); + if (!ReadProcessMemory(hProcess, params, pp, cmd_offset + 16, NULL)) + goto out; + + ucmdline = (UNICODE_STRING*)(pp + cmd_offset); + wcmdline = (PWSTR)calloc(ucmdline->Length + 1, sizeof(WCHAR)); + if (!ReadProcessMemory(hProcess, ucmdline->Buffer, wcmdline, ucmdline->Length, NULL)) { + safe_free(wcmdline); + goto out; + } + } + +out: + free(peb); + free(pp); + return wcmdline; +} static DWORD WINAPI SearchProcessThread(LPVOID param) { @@ -326,11 +433,11 @@ static DWORD WINAPI SearchProcessThread(LPVOID param) WCHAR *wHandleName = NULL; HANDLE dupHandle = NULL; HANDLE processHandle = NULL; - BOOLEAN bFound = FALSE, bGotExePath, verbose = !_bQuiet; + BOOLEAN bFound = FALSE, bGotCmdLine, verbose = !_bQuiet; ULONG access_rights = 0; DWORD size; - char exe_path[MAX_PATH] = { 0 }; - wchar_t wexe_path[MAX_PATH]; + char cmdline[MAX_PATH] = { 0 }; + wchar_t wexe_path[MAX_PATH], *wcmdline; int cur_pid; PF_INIT_OR_SET_STATUS(NtQueryObject, Ntdll); @@ -380,7 +487,7 @@ static DWORD WINAPI SearchProcessThread(LPVOID param) // If we're switching process and found a match, print it if (bFound) { - static_sprintf (tmp, "● [%06u] %s (%s)", (uint32_t)pid[cur_pid], exe_path, access_rights_str[access_rights & 0x7]); + static_sprintf (tmp, "● [%06u] %s (%s)", (uint32_t)pid[cur_pid], cmdline, access_rights_str[access_rights & 0x7]); vuprintf(tmp); StrArrayAdd(&BlockingProcess, tmp, TRUE); bFound = FALSE; @@ -411,7 +518,7 @@ static DWORD WINAPI SearchProcessThread(LPVOID param) // Open the process to which the handle we are after belongs, if not already opened if (pid[0] != pid[1]) { - status = PhOpenProcess(&processHandle, PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, + status = PhOpenProcess(&processHandle, PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, (HANDLE)handleInfo->UniqueProcessId); // There exists some processes we can't access if (!NT_SUCCESS(status)) { @@ -485,38 +592,47 @@ static DWORD WINAPI SearchProcessThread(LPVOID param) access_mask |= (BYTE) (access_rights & 0x7) + 0x80; // Bit 7 is always set if a process was found // If this is the very first process we find, print a header - if (exe_path[0] == 0) + if (cmdline[0] == 0) vuprintf("WARNING: The following process(es) or service(s) are accessing %s:", _HandleName); - // First, we try to get the executable path using GetModuleFileNameEx - bGotExePath = (GetModuleFileNameExU(processHandle, 0, exe_path, MAX_PATH - 1) != 0); + // Where possible, try to get the full command line + wcmdline = GetProcessCommandLine(processHandle); + if (wcmdline != NULL) { + bGotCmdLine = TRUE; + wchar_to_utf8_no_alloc(wcmdline, cmdline, sizeof(cmdline)); + free(wcmdline); + } + + // If we couldn't get the full commandline, try to get the executable path + if (!bGotCmdLine) + bGotCmdLine = (GetModuleFileNameExU(processHandle, 0, cmdline, MAX_PATH - 1) != 0); // The above may not work on Windows 7, so try QueryFullProcessImageName (Vista or later) - if (!bGotExePath) { + if (!bGotCmdLine) { size = MAX_PATH; PF_INIT(QueryFullProcessImageNameW, kernel32); if ( (pfQueryFullProcessImageNameW != NULL) && - (bGotExePath = pfQueryFullProcessImageNameW(processHandle, 0, wexe_path, &size)) ) - wchar_to_utf8_no_alloc(wexe_path, exe_path, sizeof(exe_path)); + (bGotCmdLine = pfQueryFullProcessImageNameW(processHandle, 0, wexe_path, &size)) ) + wchar_to_utf8_no_alloc(wexe_path, cmdline, sizeof(cmdline)); } // Still nothing? Try GetProcessImageFileName. Note that GetProcessImageFileName uses // '\Device\Harddisk#\Partition#\' instead drive letters - if (!bGotExePath) { - bGotExePath = (GetProcessImageFileNameW(processHandle, wexe_path, MAX_PATH) != 0); - if (bGotExePath) - wchar_to_utf8_no_alloc(wexe_path, exe_path, sizeof(exe_path)); + if (!bGotCmdLine) { + bGotCmdLine = (GetProcessImageFileNameW(processHandle, wexe_path, MAX_PATH) != 0); + if (bGotCmdLine) + wchar_to_utf8_no_alloc(wexe_path, cmdline, sizeof(cmdline)); } // Complete failure => Just craft a default process name that includes the PID - if (!bGotExePath) { - static_sprintf(exe_path, "Unknown_Process_%" PRIu64, + if (!bGotCmdLine) { + static_sprintf(cmdline, "Unknown_Process_%" PRIu64, (ULONGLONG)handleInfo->UniqueProcessId); } } out: - if (exe_path[0] != 0) + if (cmdline[0] != 0) vuprintf("You should close these applications before attempting to reformat the drive."); else vuprintf("NOTE: Could not identify the process(es) or service(s) accessing %s", _HandleName); diff --git a/src/process.h b/src/process.h index a0d7cf6a..963da756 100644 --- a/src/process.h +++ b/src/process.h @@ -4,9 +4,9 @@ * * Modified from Process Hacker: * https://github.com/processhacker2/processhacker2/ - * Copyright © 2009-2016 wj32 + * Copyright © 2017-2019 Pete Batard * Copyright © 2017 dmex - * Copyright © 2017 Pete Batard + * Copyright © 2009-2016 wj32 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -50,6 +50,11 @@ #define SystemExtendedHandleInformation 64 #define FileProcessIdsUsingFileInformation 47 +// MinGW doesn't know this one yet +#if !defined(PROCESSOR_ARCHITECTURE_ARM64) +#define PROCESSOR_ARCHITECTURE_ARM64 12 +#endif + #define NtCurrentProcess() ((HANDLE)(LONG_PTR)-1) typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX @@ -113,8 +118,24 @@ typedef struct _OBJECT_TYPES_INFORMATION ULONG NumberOfTypes; } OBJECT_TYPES_INFORMATION, *POBJECT_TYPES_INFORMATION; +typedef struct _PROCESS_BASIC_INFORMATION_WOW64 +{ + PVOID Reserved1[2]; + PVOID64 PebBaseAddress; + PVOID Reserved2[4]; + ULONG_PTR UniqueProcessId[2]; + PVOID Reserved3[2]; +} PROCESS_BASIC_INFORMATION_WOW64; -typedef struct _FILE_PROCESS_IDS_USING_FILE_INFORMATION { +typedef struct _UNICODE_STRING_WOW64 +{ + USHORT Length; + USHORT MaximumLength; + PVOID64 Buffer; +} UNICODE_STRING_WOW64; + +typedef struct _FILE_PROCESS_IDS_USING_FILE_INFORMATION +{ ULONG NumberOfProcessIdsInList; ULONG_PTR ProcessIdList[1]; } FILE_PROCESS_IDS_USING_FILE_INFORMATION, *PFILE_PROCESS_IDS_USING_FILE_INFORMATION; @@ -123,11 +144,11 @@ typedef struct _FILE_PROCESS_IDS_USING_FILE_INFORMATION { #define ALIGN_UP(Address, Type) ALIGN_UP_BY(Address, sizeof(Type)) #define PH_FIRST_OBJECT_TYPE(ObjectTypes) \ - (POBJECT_TYPE_INFORMATION)((PCHAR)(ObjectTypes) + ALIGN_UP(sizeof(OBJECT_TYPES_INFORMATION), ULONG_PTR)) + (POBJECT_TYPE_INFORMATION)((PCHAR)(ObjectTypes) + ALIGN_UP(sizeof(OBJECT_TYPES_INFORMATION), ULONG_PTR)) #define PH_NEXT_OBJECT_TYPE(ObjectType) \ - (POBJECT_TYPE_INFORMATION)((PCHAR)(ObjectType) + sizeof(OBJECT_TYPE_INFORMATION) + \ - ALIGN_UP(ObjectType->TypeName.MaximumLength, ULONG_PTR)) + (POBJECT_TYPE_INFORMATION)((PCHAR)(ObjectType) + sizeof(OBJECT_TYPE_INFORMATION) + \ + ALIGN_UP(ObjectType->TypeName.MaximumLength, ULONG_PTR)) // Heaps diff --git a/src/rufus.rc b/src/rufus.rc index ea45e29f..8d727819 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 232, 326 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_ACCEPTFILES -CAPTION "Rufus 3.5.1434" +CAPTION "Rufus 3.5.1435" FONT 9, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP @@ -394,8 +394,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 3,5,1434,0 - PRODUCTVERSION 3,5,1434,0 + FILEVERSION 3,5,1435,0 + PRODUCTVERSION 3,5,1435,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -413,13 +413,13 @@ BEGIN VALUE "Comments", "https://akeo.ie" VALUE "CompanyName", "Akeo Consulting" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "3.5.1434" + VALUE "FileVersion", "3.5.1435" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", " 2011-2018 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "https://www.gnu.org/copyleft/gpl.html" VALUE "OriginalFilename", "rufus-3.5.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "3.5.1434" + VALUE "ProductVersion", "3.5.1435" END END BLOCK "VarFileInfo"