You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
778 lines
27 KiB
778 lines
27 KiB
/* |
|
* Copyright (C) 2012 The Android Open Source Project |
|
* |
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
* you may not use this file except in compliance with the License. |
|
* You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
|
|
/* |
|
* "find_lock.exe", for Windows only. |
|
* |
|
* References used: |
|
* |
|
* http://drdobbs.com/windows/184411099 |
|
* article by Sven B. Schreiber, November 01, 1999 |
|
* |
|
* http://www.codeguru.com/Cpp/W-P/system/processesmodules/article.php/c2827/ |
|
* by Zoltan Csizmadia, November 14, 2000 |
|
* |
|
* http://stackoverflow.com/questions/860656/ |
|
* (same technique, but written in unsafe C#) |
|
* |
|
* Starting with Vista, we can also use the Restart Manager API as |
|
* explained here: (TODO for next version) |
|
* http://msdn.microsoft.com/en-us/magazine/cc163450.aspx |
|
*/ |
|
|
|
#ifdef _WIN32 |
|
|
|
#include "utils.h" |
|
#include <ctype.h> |
|
#include <fcntl.h> |
|
#include <io.h> |
|
#include <process.h> |
|
|
|
|
|
// NtDll structures from the the Dr Dobbs article, adjusted for our needs: |
|
|
|
typedef void *POBJECT; |
|
typedef LONG KPRIORITY; |
|
typedef LARGE_INTEGER QWORD; |
|
|
|
typedef struct { |
|
WORD Length; |
|
WORD MaximumLength; |
|
PWORD Buffer; |
|
} UNICODE_STRING; |
|
|
|
typedef struct { |
|
DWORD dIdProcess; |
|
BYTE bObjectType; // OB_TYPE_* |
|
BYTE bFlags; // bits 0..2 HANDLE_FLAG_* |
|
WORD wValue; // multiple of 4 |
|
POBJECT pObject; |
|
ACCESS_MASK GrantedAccess; |
|
} SYSTEM_HANDLE; |
|
|
|
typedef struct { |
|
DWORD dCount; |
|
SYSTEM_HANDLE ash[1]; |
|
} SYSTEM_HANDLE_INFORMATION; |
|
|
|
typedef struct { |
|
DWORD PeakVirtualSize; |
|
DWORD VirtualSize; |
|
DWORD PageFaultCount; |
|
DWORD PeakWorkingSetSize; |
|
DWORD WorkingSetSize; |
|
DWORD QuotaPeakPagedPoolUsage; |
|
DWORD QuotaPagedPoolUsage; |
|
DWORD QuotaPeakNonPagedPoolUsage; |
|
DWORD QuotaNonPagedPoolUsage; |
|
DWORD PagefileUsage; |
|
DWORD PeakPagefileUsage; |
|
} VM_COUNTERS; |
|
|
|
typedef struct { |
|
HANDLE UniqueProcess; |
|
HANDLE UniqueThread; |
|
} CLIENT_ID; |
|
|
|
typedef enum { |
|
// Ignored. We don't actually use these values. |
|
Unused |
|
} KWAIT_REASON; |
|
|
|
typedef struct { |
|
QWORD qKernelTime; // 100 nsec units |
|
QWORD qUserTime; // 100 nsec units |
|
QWORD qCreateTime; // relative to 01-01-1601 |
|
DWORD d18; |
|
PVOID pStartAddress; |
|
CLIENT_ID Cid; // process/thread ids |
|
DWORD dPriority; |
|
DWORD dBasePriority; |
|
DWORD dContextSwitches; |
|
DWORD dThreadState; // 2=running, 5=waiting |
|
KWAIT_REASON WaitReason; |
|
DWORD dReserved01; |
|
} SYSTEM_THREAD; |
|
|
|
typedef struct { |
|
DWORD dNext; // relative offset |
|
DWORD dThreadCount; |
|
DWORD dReserved01; |
|
DWORD dReserved02; |
|
DWORD dReserved03; |
|
DWORD dReserved04; |
|
DWORD dReserved05; |
|
DWORD dReserved06; |
|
QWORD qCreateTime; // relative to 01-01-1601 |
|
QWORD qUserTime; // 100 nsec units |
|
QWORD qKernelTime; // 100 nsec units |
|
UNICODE_STRING usName; |
|
KPRIORITY BasePriority; |
|
DWORD dUniqueProcessId; |
|
DWORD dInheritedFromUniqueProcessId; |
|
DWORD dHandleCount; |
|
DWORD dReserved07; |
|
DWORD dReserved08; |
|
VM_COUNTERS VmCounters; |
|
DWORD dCommitCharge; // bytes |
|
SYSTEM_THREAD ast[1]; |
|
} SYSTEM_PROCESS_INFORMATION; |
|
|
|
// The sic opcode for NtQuerySystemInformation |
|
typedef enum { |
|
SystemProcessInformation = 5, |
|
SystemHandleInformation = 16, |
|
} SYSTEMINFOCLASS; |
|
|
|
|
|
#define STATUS_SUCCESS 0x00000000 |
|
#define STATUS_UNSUCCESSFUL 0xC0000001 |
|
#define STATUS_NOT_IMPLEMENTED 0xC0000002 |
|
#define STATUS_INVALID_INFO_CLASS 0xC0000003 |
|
#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004 |
|
#define STATUS_INVALID_PARAMETER 0xC000000D |
|
|
|
typedef DWORD (WINAPI *NtQuerySystemInformationFuncPtr)( |
|
DWORD sic, VOID* pData, DWORD sSize, ULONG* pdSize); |
|
typedef DWORD (WINAPI *NtQueryInformationFileFuncPtr)(HANDLE, PVOID, PVOID, DWORD, DWORD); |
|
typedef DWORD (WINAPI *NtQueryObjectFuncPtr)(HANDLE, DWORD, VOID*, DWORD, VOID*); |
|
|
|
static NtQuerySystemInformationFuncPtr sNtQuerySystemInformationFunc; |
|
static NtQueryInformationFileFuncPtr sNtQueryInformationFileFunc; |
|
static NtQueryObjectFuncPtr sNtQueryObjectFunc; |
|
|
|
//------------ |
|
|
|
// Get the NT DLL functions we need to use. |
|
static bool init() { |
|
|
|
sNtQuerySystemInformationFunc = |
|
(NtQuerySystemInformationFuncPtr) GetProcAddress( |
|
GetModuleHandleA("ntdll.dll"), "NtQuerySystemInformation"); |
|
|
|
sNtQueryInformationFileFunc = |
|
(NtQueryInformationFileFuncPtr) GetProcAddress( |
|
GetModuleHandleA("ntdll.dll"), "NtQueryInformationFile"); |
|
|
|
sNtQueryObjectFunc = |
|
(NtQueryObjectFuncPtr) GetProcAddress( |
|
GetModuleHandleA("ntdll.dll"), "NtQueryObject"); |
|
|
|
return sNtQuerySystemInformationFunc != NULL && |
|
sNtQueryInformationFileFunc != NULL && |
|
sNtQueryObjectFunc != NULL; |
|
} |
|
|
|
static void terminate() { |
|
sNtQuerySystemInformationFunc = NULL; |
|
sNtQueryInformationFileFunc = NULL; |
|
sNtQueryObjectFunc = NULL; |
|
} |
|
|
|
static bool adjustPrivileges() { |
|
char *error = NULL; |
|
HANDLE tokenH; |
|
|
|
// Open a process token that lets us adjust privileges |
|
BOOL ok = OpenProcessToken(GetCurrentProcess(), // ProcessHandle |
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, // DesiredAccess |
|
&tokenH); // TokenHandle |
|
if (!ok) { |
|
error = "OpenProcessToken failed: "; |
|
goto bail_out; |
|
} |
|
|
|
// Lookup the privilege by name and get its local LUID token. |
|
// What we request: |
|
// SE_DEBUG_NAME, aka "SeDebugPrivilege" |
|
// MSDN: Required to debug and adjust the memory of a process owned by another account. |
|
// User Right: Debug programs. |
|
TOKEN_PRIVILEGES priv; |
|
priv.PrivilegeCount = 1; |
|
priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; |
|
ok = LookupPrivilegeValueA(NULL, // lpSystemName |
|
SE_DEBUG_NAME, // lpName |
|
&(priv.Privileges[0].Luid)); // lpLuid |
|
if (!ok) { |
|
error = "LookupPrivilegeValue failed: "; |
|
goto bail_out; |
|
} |
|
|
|
ok = AdjustTokenPrivileges(tokenH, // TokenHandle |
|
FALSE, // DisableAllPrivileges |
|
&priv, // NewState |
|
0, // BufferLength |
|
NULL, // PreviousState |
|
0); // ReturnLength |
|
if (!ok) { |
|
error = "AdjustTokenPrivileges failed: "; |
|
goto bail_out; |
|
} |
|
|
|
bail_out: |
|
if (error != NULL && gIsDebug) { |
|
CString err; |
|
err.setLastWin32Error(error); |
|
fprintf(stderr, "%s", err.cstr()); |
|
} |
|
|
|
if (tokenH != NULL) { |
|
CloseHandle(tokenH); |
|
} |
|
|
|
return !!ok; |
|
} |
|
|
|
static bool getHandleType(HANDLE h, CString *type) { |
|
bool result = false; |
|
ULONG size = 0; |
|
// Get the size of the type string |
|
int status = sNtQueryObjectFunc(h, 2, NULL, 0, &size); |
|
if (status == STATUS_INFO_LENGTH_MISMATCH && size > 0) { |
|
// Get the type string itself |
|
char *buf = new char[size]; |
|
status = sNtQueryObjectFunc(h, 2, buf, size, NULL); |
|
if (status == 0 && size > 96) { |
|
// The type string we want is a wide unicode (UTF16) |
|
// zero-terminated string located at offset 96 in the |
|
// buffer. In our case we want the string to be |
|
// "Directory" or "File" so we know the max useful length |
|
// is 9. |
|
// Since we can only deal with ansi strings in this program, |
|
// we'll make a crude copy of every other byte and just check |
|
// that the other bytes are zero. |
|
const char *c = buf + 96; |
|
const char *e = buf + 96 + size; |
|
// we'll write at the beginning of our buffer |
|
char *dest = buf; |
|
char *dend = dest + 9; |
|
for (; c < e && dest < dend && c[0] != '\0' && c[1] == '\0'; c += 2, dest++) { |
|
*dest = *c; |
|
} |
|
*(dest++) = '\0'; |
|
type->set(buf, dest - buf); |
|
result = true; |
|
} |
|
|
|
free(buf); |
|
} |
|
return result; |
|
} |
|
|
|
// These is the wide unicode representations of the type we want to find. |
|
static const char kFileW[] = "File"; |
|
|
|
static char isFileHandleType(HANDLE handle) { |
|
char type = 0; |
|
ULONG size = 0; |
|
// Get the size of the type string |
|
int status = sNtQueryObjectFunc(handle, 2, NULL, 0, &size); |
|
if (status == STATUS_INFO_LENGTH_MISMATCH && size > 0) { |
|
// Get the type string itself |
|
char *buf = new char[size]; |
|
status = sNtQueryObjectFunc(handle, 2, buf, size, NULL); |
|
if (status == 0 && size > 96) { |
|
// The type string we want is a wide unicode (UTF16-LE) |
|
// zero-terminated string located at offset 96 in the |
|
// buffer. In our case we want the string to be "File". |
|
// |
|
// Since we're reading wide unicode, we want each character |
|
// to be the one from our string followed by a zero byte. |
|
// e.g. c should point to F \0 i \0 l \0 e \0 \0 \0. |
|
const char *c = buf + 96; |
|
type = c[0]; |
|
|
|
int len = sizeof(kFileW); |
|
const char *d = kFileW; |
|
|
|
for (; type != 0 && len > 0; c+=2, d++, len--) { |
|
if (c[0] != *d || c[1] != 0) { |
|
type = 0; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
free(buf); |
|
} |
|
return type; |
|
} |
|
|
|
typedef struct { |
|
HANDLE handle; |
|
CString *outStr; |
|
bool result; |
|
} SFileNameInfo; |
|
|
|
static unsigned __stdcall FileNameThreadFunc(void *param) { |
|
SFileNameInfo *info = (SFileNameInfo *)param; |
|
if (info == NULL) { |
|
return 1; |
|
} |
|
|
|
char buf[MAX_PATH*2 + 4]; |
|
DWORD iob[2] = { 0, 0 }; |
|
|
|
DWORD status = sNtQueryInformationFileFunc(info->handle, iob, buf, sizeof(buf), 9); |
|
if (status == STATUS_SUCCESS) { |
|
// The result is a buffer with: |
|
// - DWORD (4 bytes) for the *byte* length (so twice the character length) |
|
// - Actual string in Unicode |
|
// Not sure of the actual type, but it does look like a UNICODE_STRING struct. |
|
|
|
DWORD len = ((DWORD *)buf)[0]; |
|
if (len <= MAX_PATH * 2) { |
|
// We can't handle wide Unicode. What we do is convert it into |
|
// straight ansi by just retaining the first of each couple bytes. |
|
// Bytes that cannot be mapped (e.g. 2nd byte is != 0) will be |
|
// simply converted to 0xFF. |
|
|
|
unsigned char *dest = (unsigned char *)buf + 4; |
|
unsigned char *src = (unsigned char *)buf + 4; |
|
for (DWORD i = 0; i < len; dest++, src += 2, i += 2) { |
|
if (src[1] == 0) { |
|
*dest = *src; |
|
} else { |
|
*dest = 0xFF; |
|
} |
|
} |
|
*dest = '\0'; |
|
info->outStr->set(buf + 4, len); |
|
info->result = true; |
|
return 0; |
|
} |
|
} |
|
return 1; |
|
} |
|
|
|
static bool getFileName(HANDLE handle, CString *outStr) { |
|
SFileNameInfo info; |
|
info.handle = handle; |
|
info.outStr = outStr; |
|
info.result = false; |
|
|
|
// sNtQueryInformationFileFunc might hang on some handles. |
|
// A trick is to do it in a thread and if it takes too loog then |
|
// just shutdown the thread, since it's deadlocked anyway. |
|
unsigned threadId; |
|
HANDLE th = (HANDLE)_beginthreadex(NULL, // security |
|
0, // stack_size |
|
&FileNameThreadFunc, // address |
|
&info, // arglist |
|
0, // initflag |
|
&threadId); // thrdaddr |
|
|
|
if (th == NULL) { |
|
// Failed to create thread. Shouldn't really happen. |
|
outStr->set("<failed to create thread>"); |
|
return false; |
|
} |
|
|
|
bool result = false; |
|
|
|
// Wait for thread or kill it if it takes too long. |
|
if (WaitForSingleObject(th /*handle*/, 200 /*ms*/) == WAIT_TIMEOUT) { |
|
TerminateThread(th /*handle*/, 0 /*retCode*/); |
|
outStr->set("<timeout>"); |
|
} else { |
|
result = info.result; |
|
} |
|
|
|
CloseHandle(th); |
|
return result; |
|
} |
|
|
|
// Find the name of the process (e.g. "java.exe") given its id. |
|
// processesPtr must be the list returned by getAllProcesses(). |
|
// Special handling for javaw.exe: this isn't quite useful so |
|
// we also try to find and append the parent process name. |
|
static bool getProcessName(SYSTEM_PROCESS_INFORMATION *processesPtr, |
|
DWORD remoteProcessId, |
|
CString *outStr) { |
|
SYSTEM_PROCESS_INFORMATION *ptr = processesPtr; |
|
while (ptr != NULL) { |
|
if (ptr->dUniqueProcessId == remoteProcessId) { |
|
// This is the process we want. |
|
|
|
UNICODE_STRING *uniStr = &(ptr->usName); |
|
WORD len = uniStr->Length; |
|
|
|
char buf[MAX_PATH]; |
|
if (len <= MAX_PATH * 2) { |
|
// We can't handle wide Unicode. What we do is convert it into |
|
// straight ansi by just retaining the first of each couple bytes. |
|
// Bytes that cannot be mapped (e.g. 2nd byte is != 0) will be |
|
// simply converted to 0xFF. |
|
|
|
unsigned char *dest = (unsigned char *)buf; |
|
unsigned char *src = (unsigned char *)uniStr->Buffer; |
|
for (WORD i = 0; i < len; dest++, src += 2, i += 2) { |
|
if (src[1] == 0) { |
|
*dest = *src; |
|
} else { |
|
*dest = 0xFF; |
|
} |
|
} |
|
*dest = '\0'; |
|
outStr->set(buf, len); |
|
|
|
if (strcmp(buf, "javaw.exe") == 0) { |
|
// Heuristic: eclipse often shows up as javaw.exe |
|
// but what is useful is to report eclipse to the user |
|
// instead. |
|
// So in this case, look at the parent and report it too. |
|
DWORD parentId = ptr->dInheritedFromUniqueProcessId; |
|
if (parentId > 0) { |
|
CString name2; |
|
bool ok2 = getProcessName(processesPtr, |
|
parentId, |
|
&name2); |
|
if (ok2) { |
|
outStr->add(" ("); |
|
outStr->add(name2.cstr()); |
|
outStr->add(")"); |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
} |
|
|
|
// Look at the next process, if any. |
|
if (ptr->dNext == NULL) { |
|
break; |
|
} else { |
|
ptr = (SYSTEM_PROCESS_INFORMATION *)((char *)ptr + ptr->dNext); |
|
} |
|
} |
|
|
|
outStr->setf("<process id %08x name not found>", remoteProcessId); |
|
return false; |
|
} |
|
|
|
// Query system for all processes information. |
|
// Returns an error string in case of error. |
|
// Returns the virtual_alloc-allocated buffer on success or NULL on error. |
|
// It's up to the caller to do a VirtualFree on the returned buffer. |
|
static SYSTEM_PROCESS_INFORMATION *queryAllProcess(const char **error) { |
|
// Allocate a buffer for the process information. We don't know the |
|
// exact size. A normal system might typically have between 100-200 processes. |
|
// We'll resize the buffer if not big enough. |
|
DWORD infoSize = 4096; |
|
SYSTEM_PROCESS_INFORMATION *infoPtr = |
|
(SYSTEM_PROCESS_INFORMATION *) VirtualAlloc(NULL, infoSize, MEM_COMMIT, PAGE_READWRITE); |
|
|
|
if (infoPtr != NULL) { |
|
// Query the actual size needed (or the data if it fits in the buffer) |
|
DWORD needed = 0; |
|
if (sNtQuerySystemInformationFunc( |
|
SystemProcessInformation, infoPtr, infoSize, &needed) != 0) { |
|
if (needed == 0) { |
|
// Shouldn't happen. |
|
*error = "No processes found"; |
|
goto bail_out; |
|
} |
|
|
|
// Realloc |
|
VirtualFree(infoPtr, 0, MEM_RELEASE); |
|
infoSize += needed; |
|
infoPtr = (SYSTEM_PROCESS_INFORMATION *) VirtualAlloc( |
|
NULL, infoSize, MEM_COMMIT, PAGE_READWRITE); |
|
|
|
// Query all the processes objects again |
|
if (sNtQuerySystemInformationFunc( |
|
SystemProcessInformation, infoPtr, infoSize, NULL) != 0) { |
|
*error = "Failed to query system processes"; |
|
goto bail_out; |
|
} |
|
} |
|
} |
|
|
|
if (infoPtr == NULL) { |
|
*error = "Failed to allocate system processes info buffer"; |
|
goto bail_out; |
|
} |
|
|
|
bail_out: |
|
if (*error != NULL) { |
|
VirtualFree(infoPtr, 0, MEM_RELEASE); |
|
infoPtr = NULL; |
|
} |
|
return infoPtr; |
|
} |
|
|
|
// Query system for all handle information. |
|
// Returns an error string in case of error. |
|
// Returns the virtual_alloc-allocated buffer on success or NULL on error. |
|
// It's up to the caller to do a VirtualFree on the returned buffer. |
|
static SYSTEM_HANDLE_INFORMATION *queryAllHandles(const char **error) { |
|
// Allocate a buffer. It won't be large enough to get the handles |
|
// (e.g. there might be 10k or 40k handles around). We'll resize |
|
// it once we know the actual size. |
|
DWORD infoSize = 4096; |
|
SYSTEM_HANDLE_INFORMATION *infoPtr = |
|
(SYSTEM_HANDLE_INFORMATION *) VirtualAlloc(NULL, infoSize, MEM_COMMIT, PAGE_READWRITE); |
|
|
|
if (infoPtr != NULL) { |
|
// Query the actual size needed |
|
DWORD needed = 0; |
|
if (sNtQuerySystemInformationFunc( |
|
SystemHandleInformation, infoPtr, infoSize, &needed) != 0) { |
|
if (needed == 0) { |
|
// Shouldn't happen. |
|
*error = "No handles found"; |
|
goto bail_out; |
|
} |
|
|
|
// Realloc |
|
VirtualFree(infoPtr, 0, MEM_RELEASE); |
|
infoSize += needed; |
|
infoPtr = (SYSTEM_HANDLE_INFORMATION *) VirtualAlloc( |
|
NULL, infoSize, MEM_COMMIT, PAGE_READWRITE); |
|
} |
|
} |
|
|
|
if (infoPtr == NULL) { |
|
*error = "Failed to allocate system handle info buffer"; |
|
goto bail_out; |
|
} |
|
|
|
// Query all the handle objects |
|
if (sNtQuerySystemInformationFunc(SystemHandleInformation, infoPtr, infoSize, NULL) != 0) { |
|
*error = "Failed to query system handles"; |
|
goto bail_out; |
|
} |
|
|
|
bail_out: |
|
if (*error != NULL) { |
|
VirtualFree(infoPtr, 0, MEM_RELEASE); |
|
infoPtr = NULL; |
|
} |
|
return infoPtr; |
|
} |
|
|
|
bool findLock(CPath &path, CString *outModule) { |
|
bool result = false; |
|
const char *error = NULL; |
|
|
|
SYSTEM_PROCESS_INFORMATION *processesPtr = NULL; |
|
SYSTEM_HANDLE_INFORMATION *handlesPtr = NULL; |
|
|
|
const HANDLE currProcessH = GetCurrentProcess(); |
|
const DWORD currProcessId = GetCurrentProcessId(); |
|
HANDLE remoteProcessH = NULL; |
|
DWORD remoteProcessId = 0; |
|
DWORD matchProcessId = 0; |
|
|
|
int numHandleFound = 0; |
|
int numHandleChecked = 0; |
|
int numHandleDirs = 0; |
|
int numHandleFiles = 0; |
|
int numProcessMatch = 0; |
|
|
|
BYTE ob_type_file = 0; |
|
|
|
// Get the path to search, without the drive letter. |
|
const char *searchPath = path.cstr(); |
|
if (isalpha(searchPath[0]) && searchPath[1] == ':') { |
|
searchPath += 2; |
|
} |
|
size_t searchPathLen = strlen(searchPath); |
|
|
|
if (gIsDebug) fprintf(stderr, "Search path: '%s'\n", searchPath); |
|
|
|
if (!init()) { |
|
error = "Failed to bind to ntdll.dll"; |
|
goto bail_out; |
|
} |
|
|
|
if (!adjustPrivileges()) { |
|
// We can still continue even if the privilege escalation failed. |
|
// The apparent effect is that we'll fail to query the name of |
|
// some processes, yet it will work for some of them. |
|
if (gIsDebug) fprintf(stderr, "Warning: adusting privileges failed. Continuing anyway.\n"); |
|
} else { |
|
if (gIsDebug) fprintf(stderr, "Privileges adjusted.\n"); // DEBUG remove lter |
|
} |
|
|
|
processesPtr = queryAllProcess(&error); |
|
if (processesPtr == NULL) goto bail_out; |
|
|
|
handlesPtr = queryAllHandles(&error); |
|
if (handlesPtr == NULL) goto bail_out; |
|
|
|
numHandleFound = handlesPtr->dCount; |
|
|
|
// Check all the handles |
|
for (int n = handlesPtr->dCount, i = 0; i < n; i++) { |
|
SYSTEM_HANDLE sysh = handlesPtr->ash[i]; |
|
|
|
if (ob_type_file != 0 && sysh.bObjectType != ob_type_file) { |
|
continue; |
|
} |
|
|
|
HANDLE handle = (HANDLE) sysh.wValue; |
|
DWORD remoteId = sysh.dIdProcess; |
|
HANDLE remoteH = NULL; |
|
|
|
if (remoteId == matchProcessId) { |
|
// We already matched that process, we can skip its other entries. |
|
continue; |
|
} |
|
|
|
if (remoteId == currProcessId) { |
|
// We don't match ourselves |
|
continue; |
|
} |
|
|
|
// Open a remote process. |
|
// Most entries of a given process seem to be consecutive, so we |
|
// only open the remote process handle if it's a different id. |
|
if (remoteProcessH == NULL && remoteId == remoteProcessId) { |
|
// We already tried to open this process and it failed. |
|
// It's not going to be any better the next time so skip it. |
|
continue; |
|
} |
|
if (remoteProcessH == NULL || remoteId != remoteProcessId) { |
|
if (remoteProcessH != NULL) { |
|
CloseHandle(remoteProcessH); |
|
} |
|
|
|
remoteProcessId = remoteId; |
|
remoteProcessH = OpenProcess(PROCESS_DUP_HANDLE, |
|
FALSE /*inheritHandle*/, |
|
remoteProcessId); |
|
if (remoteProcessH == NULL) { |
|
continue; |
|
} |
|
} |
|
|
|
if (remoteProcessH != NULL) { |
|
// Duplicate the remote handle |
|
if (DuplicateHandle(remoteProcessH, // hSourceProcessHandle |
|
handle, // hSourceHandle |
|
currProcessH, // hTargetProcessHandle |
|
&remoteH, // lpTargetHandle |
|
0, // dwDesiredAccess (ignored by same access) |
|
FALSE, // bInheritHandle |
|
DUPLICATE_SAME_ACCESS) == 0) { |
|
continue; |
|
} |
|
} |
|
|
|
numHandleChecked++; |
|
|
|
char type = isFileHandleType(remoteH); |
|
|
|
if (type != 0) { |
|
if (type == 'D') numHandleDirs++; |
|
else if (type == 'F') numHandleFiles++; |
|
|
|
// TODO simplify by not keeping directory handles |
|
if (ob_type_file == 0 && type == 'F') { |
|
// We found the first file handle. Remember it's system_handle object type |
|
// and then use it to filter the following system_handle. |
|
// For some reason OB_TYPE_FILE should be 0x1A but empirically I find it |
|
// to be 0x1C, so we just make this test more dynamic. |
|
ob_type_file = sysh.bObjectType; |
|
} |
|
|
|
// Try to get a filename out of that file or directory handle. |
|
CString name("<unknown>"); |
|
bool ok = getFileName(remoteH, &name); |
|
|
|
if (gIsDebug) { |
|
fprintf(stderr, "P:%08x | t:%02x | f:%02x | v:%08x | %c | %s %s\n", |
|
sysh.dIdProcess, sysh.bObjectType, sysh.bFlags, sysh.wValue, |
|
type, |
|
ok ? "OK" : "FAIL", |
|
name.cstr() |
|
); |
|
} |
|
|
|
if (ok) { |
|
// We got a file path. Let's check if it matches our target path. |
|
if (_strnicmp(searchPath, name.cstr(), searchPathLen) == 0) { |
|
// Remember this process id so that we can ignore all its following entries. |
|
matchProcessId = remoteId; |
|
|
|
// Find out its process name |
|
CString procName("<unknown>"); |
|
ok = getProcessName(processesPtr, remoteProcessId, &procName); |
|
if (ok) { |
|
numProcessMatch++; |
|
|
|
if (!outModule->isEmpty()) { |
|
outModule->add(";"); |
|
} |
|
outModule->add(procName.cstr()); |
|
result = true; |
|
} |
|
|
|
if (gIsDebug) { |
|
fprintf(stderr, "==> MATCH FOUND: %s %s\n", |
|
ok ? "OK" : "FAIL", |
|
procName.cstr() |
|
); |
|
} |
|
} |
|
} |
|
|
|
} |
|
|
|
if (remoteH != NULL) { |
|
CloseHandle(remoteH); |
|
remoteH = NULL; |
|
} |
|
} |
|
|
|
bail_out: |
|
|
|
if (gIsDebug) { |
|
fprintf(stderr, "Processes matched: %d\n", numProcessMatch); |
|
fprintf(stderr, "Handles: %d found, %d checked, %d dirs, %d files\n", |
|
numHandleFound, |
|
numHandleChecked, |
|
numHandleDirs, |
|
numHandleFiles); |
|
} |
|
|
|
if (error != NULL) { |
|
CString msg; |
|
msg.setLastWin32Error(NULL); |
|
if (gIsDebug) fprintf(stderr, "[ERROR] %s: %s", error, msg.cstr()); |
|
} |
|
|
|
if (remoteProcessH != NULL) { |
|
CloseHandle(remoteProcessH); |
|
} |
|
|
|
if (currProcessH != NULL) { |
|
CloseHandle(currProcessH); |
|
} |
|
|
|
if (handlesPtr != NULL) { |
|
VirtualFree(handlesPtr, 0, MEM_RELEASE); |
|
handlesPtr = NULL; |
|
} |
|
|
|
terminate(); |
|
|
|
return result; |
|
} |
|
|
|
#endif /* _WIN32 */
|
|
|