1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18  * "find_lock.exe", for Windows only.
19  *
20  * References used:
21  *
22  * http://drdobbs.com/windows/184411099
23  * article by Sven B. Schreiber, November 01, 1999
24  *
25  * http://www.codeguru.com/Cpp/W-P/system/processesmodules/article.php/c2827/
26  * by Zoltan Csizmadia, November 14, 2000
27  *
28  * http://stackoverflow.com/questions/860656/
29  * (same technique, but written in unsafe C#)
30  *
31  * Starting with Vista, we can also use the Restart Manager API as
32  * explained here: (TODO for next version)
33  * http://msdn.microsoft.com/en-us/magazine/cc163450.aspx
34  */
35 
36 #ifdef _WIN32
37 
38 #include "utils.h"
39 #include <ctype.h>
40 #include <fcntl.h>
41 #include <io.h>
42 #include <process.h>
43 
44 
45 // NtDll structures from the the Dr Dobbs article, adjusted for our needs:
46 
47 typedef void *POBJECT;
48 typedef LONG KPRIORITY;
49 typedef LARGE_INTEGER QWORD;
50 
51 typedef struct {
52     WORD  Length;
53     WORD  MaximumLength;
54     PWORD Buffer;
55 } UNICODE_STRING;
56 
57 typedef struct {
58     DWORD       dIdProcess;
59     BYTE        bObjectType;    // OB_TYPE_*
60     BYTE        bFlags;         // bits 0..2 HANDLE_FLAG_*
61     WORD        wValue;         // multiple of 4
62     POBJECT     pObject;
63     ACCESS_MASK GrantedAccess;
64 } SYSTEM_HANDLE;
65 
66 typedef struct {
67     DWORD         dCount;
68     SYSTEM_HANDLE ash[1];
69 } SYSTEM_HANDLE_INFORMATION;
70 
71 typedef struct {
72     DWORD PeakVirtualSize;
73     DWORD VirtualSize;
74     DWORD PageFaultCount;
75     DWORD PeakWorkingSetSize;
76     DWORD WorkingSetSize;
77     DWORD QuotaPeakPagedPoolUsage;
78     DWORD QuotaPagedPoolUsage;
79     DWORD QuotaPeakNonPagedPoolUsage;
80     DWORD QuotaNonPagedPoolUsage;
81     DWORD PagefileUsage;
82     DWORD PeakPagefileUsage;
83 } VM_COUNTERS;
84 
85 typedef struct {
86     HANDLE UniqueProcess;
87     HANDLE UniqueThread;
88 } CLIENT_ID;
89 
90 typedef enum {
91     // Ignored. We don't actually use these values.
92     Unused
93 } KWAIT_REASON;
94 
95 typedef struct {
96     QWORD        qKernelTime;       // 100 nsec units
97     QWORD        qUserTime;         // 100 nsec units
98     QWORD        qCreateTime;       // relative to 01-01-1601
99     DWORD        d18;
100     PVOID        pStartAddress;
101     CLIENT_ID    Cid;               // process/thread ids
102     DWORD        dPriority;
103     DWORD        dBasePriority;
104     DWORD        dContextSwitches;
105     DWORD        dThreadState;      // 2=running, 5=waiting
106     KWAIT_REASON WaitReason;
107     DWORD        dReserved01;
108 } SYSTEM_THREAD;
109 
110 typedef struct {
111     DWORD          dNext;           // relative offset
112     DWORD          dThreadCount;
113     DWORD          dReserved01;
114     DWORD          dReserved02;
115     DWORD          dReserved03;
116     DWORD          dReserved04;
117     DWORD          dReserved05;
118     DWORD          dReserved06;
119     QWORD          qCreateTime;     // relative to 01-01-1601
120     QWORD          qUserTime;       // 100 nsec units
121     QWORD          qKernelTime;     // 100 nsec units
122     UNICODE_STRING usName;
123     KPRIORITY      BasePriority;
124     DWORD          dUniqueProcessId;
125     DWORD          dInheritedFromUniqueProcessId;
126     DWORD          dHandleCount;
127     DWORD          dReserved07;
128     DWORD          dReserved08;
129     VM_COUNTERS    VmCounters;
130     DWORD          dCommitCharge;   // bytes
131     SYSTEM_THREAD  ast[1];
132 } SYSTEM_PROCESS_INFORMATION;
133 
134 // The sic opcode for NtQuerySystemInformation
135 typedef enum {
136     SystemProcessInformation = 5,
137     SystemHandleInformation = 16,
138 } SYSTEMINFOCLASS;
139 
140 
141 #define STATUS_SUCCESS               0x00000000
142 #define STATUS_UNSUCCESSFUL          0xC0000001
143 #define STATUS_NOT_IMPLEMENTED       0xC0000002
144 #define STATUS_INVALID_INFO_CLASS    0xC0000003
145 #define STATUS_INFO_LENGTH_MISMATCH  0xC0000004
146 #define STATUS_INVALID_PARAMETER     0xC000000D
147 
148 typedef DWORD (WINAPI *NtQuerySystemInformationFuncPtr)(
149                                       DWORD sic, VOID* pData, DWORD sSize, ULONG* pdSize);
150 typedef DWORD (WINAPI *NtQueryInformationFileFuncPtr)(HANDLE, PVOID, PVOID, DWORD, DWORD);
151 typedef DWORD (WINAPI *NtQueryObjectFuncPtr)(HANDLE, DWORD, VOID*, DWORD, VOID*);
152 
153 static NtQuerySystemInformationFuncPtr sNtQuerySystemInformationFunc;
154 static NtQueryInformationFileFuncPtr   sNtQueryInformationFileFunc;
155 static NtQueryObjectFuncPtr            sNtQueryObjectFunc;
156 
157 //------------
158 
159 // Get the NT DLL functions we need to use.
init()160 static bool init() {
161 
162     sNtQuerySystemInformationFunc =
163         (NtQuerySystemInformationFuncPtr) GetProcAddress(
164             GetModuleHandleA("ntdll.dll"), "NtQuerySystemInformation");
165 
166     sNtQueryInformationFileFunc =
167         (NtQueryInformationFileFuncPtr) GetProcAddress(
168             GetModuleHandleA("ntdll.dll"), "NtQueryInformationFile");
169 
170     sNtQueryObjectFunc =
171         (NtQueryObjectFuncPtr) GetProcAddress(
172             GetModuleHandleA("ntdll.dll"), "NtQueryObject");
173 
174     return sNtQuerySystemInformationFunc != NULL &&
175            sNtQueryInformationFileFunc   != NULL &&
176            sNtQueryObjectFunc            != NULL;
177 }
178 
terminate()179 static void terminate() {
180     sNtQuerySystemInformationFunc = NULL;
181     sNtQueryInformationFileFunc = NULL;
182     sNtQueryObjectFunc = NULL;
183 }
184 
adjustPrivileges()185 static bool adjustPrivileges() {
186     char *error = NULL;
187     HANDLE tokenH;
188 
189     // Open a process token that lets us adjust privileges
190     BOOL ok = OpenProcessToken(GetCurrentProcess(),   // ProcessHandle
191                                TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, // DesiredAccess
192                                &tokenH);              // TokenHandle
193     if (!ok) {
194         error = "OpenProcessToken failed: ";
195         goto bail_out;
196     }
197 
198     // Lookup the privilege by name and get its local LUID token.
199     // What we request:
200     // SE_DEBUG_NAME, aka "SeDebugPrivilege"
201     // MSDN: Required to debug and adjust the memory of a process owned by another account.
202     //       User Right: Debug programs.
203     TOKEN_PRIVILEGES priv;
204     priv.PrivilegeCount = 1;
205     priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
206     ok = LookupPrivilegeValueA(NULL,                        // lpSystemName
207                                SE_DEBUG_NAME,               // lpName
208                                &(priv.Privileges[0].Luid)); // lpLuid
209     if (!ok) {
210         error = "LookupPrivilegeValue failed: ";
211         goto bail_out;
212     }
213 
214     ok = AdjustTokenPrivileges(tokenH,  // TokenHandle
215                                FALSE,   // DisableAllPrivileges
216                                &priv,   // NewState
217                                0,       // BufferLength
218                                NULL,    // PreviousState
219                                0);      // ReturnLength
220     if (!ok) {
221         error = "AdjustTokenPrivileges failed: ";
222         goto bail_out;
223     }
224 
225 bail_out:
226     if (error != NULL && gIsDebug) {
227         CString err;
228         err.setLastWin32Error(error);
229         fprintf(stderr, "%s", err.cstr());
230     }
231 
232     if (tokenH != NULL) {
233         CloseHandle(tokenH);
234     }
235 
236     return !!ok;
237 }
238 
getHandleType(HANDLE h,CString * type)239 static bool getHandleType(HANDLE h, CString *type) {
240     bool result = false;
241     ULONG size = 0;
242     // Get the size of the type string
243     int status = sNtQueryObjectFunc(h, 2, NULL, 0, &size);
244     if (status == STATUS_INFO_LENGTH_MISMATCH && size > 0) {
245         // Get the type string itself
246         char *buf = new char[size];
247         status = sNtQueryObjectFunc(h, 2, buf, size, NULL);
248         if (status == 0 && size > 96) {
249             // The type string we want is a wide unicode (UTF16)
250             // zero-terminated string located at offset 96 in the
251             // buffer. In our case we want the string to be
252             // "Directory" or "File" so we know the max useful length
253             // is 9.
254             // Since we can only deal with ansi strings in this program,
255             // we'll make a crude copy of every other byte and just check
256             // that the other bytes are zero.
257             const char *c = buf + 96;
258             const char *e = buf + 96 + size;
259             // we'll write at the beginning of our buffer
260             char *dest = buf;
261             char *dend = dest + 9;
262             for (; c < e && dest < dend && c[0] != '\0' && c[1] == '\0'; c += 2, dest++) {
263                 *dest = *c;
264             }
265             *(dest++) = '\0';
266             type->set(buf, dest - buf);
267             result = true;
268         }
269 
270         free(buf);
271     }
272     return result;
273 }
274 
275 // These is the wide unicode representations of the type we want to find.
276 static const char kFileW[] = "File";
277 
isFileHandleType(HANDLE handle)278 static char isFileHandleType(HANDLE handle) {
279     char type = 0;
280     ULONG size = 0;
281     // Get the size of the type string
282     int status = sNtQueryObjectFunc(handle, 2, NULL, 0, &size);
283     if (status == STATUS_INFO_LENGTH_MISMATCH && size > 0) {
284         // Get the type string itself
285         char *buf = new char[size];
286         status = sNtQueryObjectFunc(handle, 2, buf, size, NULL);
287         if (status == 0 && size > 96) {
288             // The type string we want is a wide unicode (UTF16-LE)
289             // zero-terminated string located at offset 96 in the
290             // buffer. In our case we want the string to be "File".
291             //
292             // Since we're reading wide unicode, we want each character
293             // to be the one from our string followed by a zero byte.
294             // e.g. c should point to F \0 i \0 l \0 e \0 \0 \0.
295             const char *c = buf + 96;
296             type = c[0];
297 
298             int len = sizeof(kFileW);
299             const char *d = kFileW;
300 
301             for (; type != 0 && len > 0; c+=2, d++, len--) {
302                 if (c[0] != *d || c[1] != 0) {
303                     type = 0;
304                     break;
305                 }
306             }
307         }
308 
309         free(buf);
310     }
311     return type;
312 }
313 
314 typedef struct {
315     HANDLE handle;
316     CString *outStr;
317     bool result;
318 } SFileNameInfo;
319 
FileNameThreadFunc(void * param)320 static unsigned __stdcall FileNameThreadFunc(void *param) {
321     SFileNameInfo *info = (SFileNameInfo *)param;
322     if (info == NULL) {
323         return 1;
324     }
325 
326     char buf[MAX_PATH*2 + 4];
327     DWORD iob[2] = { 0, 0 };
328 
329     DWORD status = sNtQueryInformationFileFunc(info->handle, iob, buf, sizeof(buf), 9);
330     if (status == STATUS_SUCCESS) {
331         // The result is a buffer with:
332         // - DWORD (4 bytes) for the *byte* length (so twice the character length)
333         // - Actual string in Unicode
334         // Not sure of the actual type, but it does look like a UNICODE_STRING struct.
335 
336         DWORD len = ((DWORD *)buf)[0];
337         if (len <= MAX_PATH * 2) {
338             // We can't handle wide Unicode. What we do is convert it into
339             // straight ansi by just retaining the first of each couple bytes.
340             // Bytes that cannot be mapped (e.g. 2nd byte is != 0) will be
341             // simply converted to 0xFF.
342 
343             unsigned char *dest = (unsigned char *)buf + 4;
344             unsigned char *src  = (unsigned char *)buf + 4;
345             for (DWORD i = 0; i < len; dest++, src += 2, i += 2) {
346                 if (src[1] == 0) {
347                     *dest = *src;
348                 } else {
349                     *dest = 0xFF;
350                 }
351             }
352             *dest = '\0';
353             info->outStr->set(buf + 4, len);
354             info->result = true;
355             return 0;
356         }
357     }
358     return 1;
359 }
360 
getFileName(HANDLE handle,CString * outStr)361 static bool getFileName(HANDLE handle, CString *outStr) {
362     SFileNameInfo info;
363     info.handle = handle;
364     info.outStr = outStr;
365     info.result = false;
366 
367     // sNtQueryInformationFileFunc might hang on some handles.
368     // A trick is to do it in a thread and if it takes too loog then
369     // just shutdown the thread, since it's deadlocked anyway.
370     unsigned threadId;
371     HANDLE th = (HANDLE)_beginthreadex(NULL,                    // security
372                                        0,                       // stack_size
373                                        &FileNameThreadFunc,     // address
374                                        &info,                   // arglist
375                                        0,                       // initflag
376                                        &threadId);              // thrdaddr
377 
378     if (th == NULL) {
379         // Failed to create thread. Shouldn't really happen.
380         outStr->set("<failed to create thread>");
381         return false;
382     }
383 
384     bool result = false;
385 
386     // Wait for thread or kill it if it takes too long.
387     if (WaitForSingleObject(th /*handle*/, 200 /*ms*/) == WAIT_TIMEOUT) {
388         TerminateThread(th /*handle*/, 0 /*retCode*/);
389         outStr->set("<timeout>");
390     } else {
391         result = info.result;
392     }
393 
394     CloseHandle(th);
395     return result;
396 }
397 
398 // Find the name of the process (e.g. "java.exe") given its id.
399 // processesPtr must be the list returned by getAllProcesses().
400 // Special handling for javaw.exe: this isn't quite useful so
401 // we also try to find and append the parent process name.
getProcessName(SYSTEM_PROCESS_INFORMATION * processesPtr,DWORD remoteProcessId,CString * outStr)402 static bool getProcessName(SYSTEM_PROCESS_INFORMATION *processesPtr,
403                            DWORD remoteProcessId,
404                            CString *outStr) {
405     SYSTEM_PROCESS_INFORMATION *ptr = processesPtr;
406     while (ptr != NULL) {
407         if (ptr->dUniqueProcessId == remoteProcessId) {
408             // This is the process we want.
409 
410             UNICODE_STRING *uniStr = &(ptr->usName);
411             WORD len = uniStr->Length;
412 
413             char buf[MAX_PATH];
414             if (len <= MAX_PATH * 2) {
415                 // We can't handle wide Unicode. What we do is convert it into
416                 // straight ansi by just retaining the first of each couple bytes.
417                 // Bytes that cannot be mapped (e.g. 2nd byte is != 0) will be
418                 // simply converted to 0xFF.
419 
420                 unsigned char *dest = (unsigned char *)buf;
421                 unsigned char *src  = (unsigned char *)uniStr->Buffer;
422                 for (WORD i = 0; i < len; dest++, src += 2, i += 2) {
423                     if (src[1] == 0) {
424                         *dest = *src;
425                     } else {
426                         *dest = 0xFF;
427                     }
428                 }
429                 *dest = '\0';
430                 outStr->set(buf, len);
431 
432                 if (strcmp(buf, "javaw.exe") == 0) {
433                     // Heuristic: eclipse often shows up as javaw.exe
434                     // but what is useful is to report eclipse to the user
435                     // instead.
436                     // So in this case, look at the parent and report it too.
437                     DWORD parentId = ptr->dInheritedFromUniqueProcessId;
438                     if (parentId > 0) {
439                         CString name2;
440                         bool ok2 = getProcessName(processesPtr,
441                                                   parentId,
442                                                   &name2);
443                         if (ok2) {
444                             outStr->add(" (");
445                             outStr->add(name2.cstr());
446                             outStr->add(")");
447                         }
448                     }
449                 }
450 
451                 return true;
452             }
453         }
454 
455         // Look at the next process, if any.
456         if (ptr->dNext == NULL) {
457             break;
458         } else {
459             ptr = (SYSTEM_PROCESS_INFORMATION *)((char *)ptr + ptr->dNext);
460         }
461     }
462 
463     outStr->setf("<process id %08x name not found>", remoteProcessId);
464     return false;
465 }
466 
467 // Query system for all processes information.
468 // Returns an error string in case of error.
469 // Returns the virtual_alloc-allocated buffer on success or NULL on error.
470 // It's up to the caller to do a VirtualFree on the returned buffer.
queryAllProcess(const char ** error)471 static SYSTEM_PROCESS_INFORMATION *queryAllProcess(const char **error) {
472     // Allocate a buffer for the process information. We don't know the
473     // exact size. A normal system might typically have between 100-200 processes.
474     // We'll resize the buffer if not big enough.
475     DWORD infoSize = 4096;
476     SYSTEM_PROCESS_INFORMATION *infoPtr =
477         (SYSTEM_PROCESS_INFORMATION *) VirtualAlloc(NULL, infoSize, MEM_COMMIT, PAGE_READWRITE);
478 
479     if (infoPtr != NULL) {
480         // Query the actual size needed (or the data if it fits in the buffer)
481         DWORD needed = 0;
482         if (sNtQuerySystemInformationFunc(
483                 SystemProcessInformation, infoPtr, infoSize, &needed) != 0) {
484             if (needed == 0) {
485                 // Shouldn't happen.
486                 *error = "No processes found";
487                 goto bail_out;
488             }
489 
490             // Realloc
491             VirtualFree(infoPtr, 0, MEM_RELEASE);
492             infoSize += needed;
493             infoPtr = (SYSTEM_PROCESS_INFORMATION *) VirtualAlloc(
494                             NULL, infoSize, MEM_COMMIT, PAGE_READWRITE);
495 
496             // Query all the processes objects again
497             if (sNtQuerySystemInformationFunc(
498                     SystemProcessInformation, infoPtr, infoSize, NULL) != 0) {
499                 *error = "Failed to query system processes";
500                 goto bail_out;
501             }
502         }
503     }
504 
505     if (infoPtr == NULL) {
506         *error = "Failed to allocate system processes info buffer";
507         goto bail_out;
508     }
509 
510 bail_out:
511     if (*error != NULL) {
512         VirtualFree(infoPtr, 0, MEM_RELEASE);
513         infoPtr = NULL;
514     }
515     return infoPtr;
516 }
517 
518 // Query system for all handle information.
519 // Returns an error string in case of error.
520 // Returns the virtual_alloc-allocated buffer on success or NULL on error.
521 // It's up to the caller to do a VirtualFree on the returned buffer.
queryAllHandles(const char ** error)522 static SYSTEM_HANDLE_INFORMATION *queryAllHandles(const char **error) {
523     // Allocate a buffer. It won't be large enough to get the handles
524     // (e.g. there might be 10k or 40k handles around). We'll resize
525     // it once we know the actual size.
526     DWORD infoSize = 4096;
527     SYSTEM_HANDLE_INFORMATION *infoPtr =
528         (SYSTEM_HANDLE_INFORMATION *) VirtualAlloc(NULL, infoSize, MEM_COMMIT, PAGE_READWRITE);
529 
530     if (infoPtr != NULL) {
531         // Query the actual size needed
532         DWORD needed = 0;
533         if (sNtQuerySystemInformationFunc(
534                 SystemHandleInformation, infoPtr, infoSize, &needed) != 0) {
535             if (needed == 0) {
536                 // Shouldn't happen.
537                 *error = "No handles found";
538                 goto bail_out;
539             }
540 
541             // Realloc
542             VirtualFree(infoPtr, 0, MEM_RELEASE);
543             infoSize += needed;
544             infoPtr = (SYSTEM_HANDLE_INFORMATION *) VirtualAlloc(
545                             NULL, infoSize, MEM_COMMIT, PAGE_READWRITE);
546         }
547     }
548 
549     if (infoPtr == NULL) {
550         *error = "Failed to allocate system handle info buffer";
551         goto bail_out;
552     }
553 
554     // Query all the handle objects
555     if (sNtQuerySystemInformationFunc(SystemHandleInformation, infoPtr, infoSize, NULL) != 0) {
556         *error = "Failed to query system handles";
557         goto bail_out;
558     }
559 
560 bail_out:
561     if (*error != NULL) {
562         VirtualFree(infoPtr, 0, MEM_RELEASE);
563         infoPtr = NULL;
564     }
565     return infoPtr;
566 }
567 
findLock(CPath & path,CString * outModule)568 bool findLock(CPath &path, CString *outModule) {
569     bool result = false;
570     const char *error = NULL;
571 
572     SYSTEM_PROCESS_INFORMATION *processesPtr = NULL;
573     SYSTEM_HANDLE_INFORMATION  *handlesPtr   = NULL;
574 
575     const HANDLE currProcessH = GetCurrentProcess();
576     const DWORD currProcessId = GetCurrentProcessId();
577     HANDLE remoteProcessH = NULL;
578     DWORD remoteProcessId = 0;
579     DWORD matchProcessId = 0;
580 
581     int numHandleFound = 0;
582     int numHandleChecked = 0;
583     int numHandleDirs = 0;
584     int numHandleFiles = 0;
585     int numProcessMatch = 0;
586 
587     BYTE ob_type_file = 0;
588 
589     // Get the path to search, without the drive letter.
590     const char *searchPath = path.cstr();
591     if (isalpha(searchPath[0]) && searchPath[1] == ':') {
592         searchPath += 2;
593     }
594     size_t searchPathLen = strlen(searchPath);
595 
596     if (gIsDebug) fprintf(stderr, "Search path: '%s'\n", searchPath);
597 
598     if (!init()) {
599         error = "Failed to bind to ntdll.dll";
600         goto bail_out;
601     }
602 
603     if (!adjustPrivileges()) {
604         // We can still continue even if the privilege escalation failed.
605         // The apparent effect is that we'll fail to query the name of
606         // some processes, yet it will work for some of them.
607         if (gIsDebug) fprintf(stderr, "Warning: adusting privileges failed. Continuing anyway.\n");
608     } else {
609         if (gIsDebug) fprintf(stderr, "Privileges adjusted.\n"); // DEBUG remove lter
610     }
611 
612     processesPtr = queryAllProcess(&error);
613     if (processesPtr == NULL) goto bail_out;
614 
615     handlesPtr = queryAllHandles(&error);
616     if (handlesPtr == NULL) goto bail_out;
617 
618     numHandleFound = handlesPtr->dCount;
619 
620     // Check all the handles
621     for (int n = handlesPtr->dCount, i = 0; i < n; i++) {
622         SYSTEM_HANDLE sysh = handlesPtr->ash[i];
623 
624         if (ob_type_file != 0 && sysh.bObjectType != ob_type_file) {
625             continue;
626         }
627 
628         HANDLE handle = (HANDLE) sysh.wValue;
629         DWORD remoteId = sysh.dIdProcess;
630         HANDLE remoteH = NULL;
631 
632         if (remoteId == matchProcessId) {
633             // We already matched that process, we can skip its other entries.
634             continue;
635         }
636 
637         if (remoteId == currProcessId) {
638             // We don't match ourselves
639             continue;
640         }
641 
642         // Open a remote process.
643         // Most entries of a given process seem to be consecutive, so we
644         // only open the remote process handle if it's a different id.
645         if (remoteProcessH == NULL && remoteId == remoteProcessId) {
646             // We already tried to open this process and it failed.
647             // It's not going to be any better the next time so skip it.
648             continue;
649         }
650         if (remoteProcessH == NULL || remoteId != remoteProcessId) {
651             if (remoteProcessH != NULL) {
652                 CloseHandle(remoteProcessH);
653             }
654 
655             remoteProcessId = remoteId;
656             remoteProcessH = OpenProcess(PROCESS_DUP_HANDLE,
657                                          FALSE /*inheritHandle*/,
658                                          remoteProcessId);
659             if (remoteProcessH == NULL) {
660                 continue;
661             }
662         }
663 
664         if (remoteProcessH != NULL) {
665             // Duplicate the remote handle
666             if (DuplicateHandle(remoteProcessH,     // hSourceProcessHandle
667                                 handle,             // hSourceHandle
668                                 currProcessH,       // hTargetProcessHandle
669                                 &remoteH,           // lpTargetHandle
670                                 0,                  // dwDesiredAccess (ignored by same access)
671                                 FALSE,              // bInheritHandle
672                                 DUPLICATE_SAME_ACCESS) == 0) {
673                 continue;
674             }
675         }
676 
677         numHandleChecked++;
678 
679         char type = isFileHandleType(remoteH);
680 
681         if (type != 0) {
682             if (type == 'D') numHandleDirs++;
683             else if (type == 'F') numHandleFiles++;
684 
685             // TODO simplify by not keeping directory handles
686             if (ob_type_file == 0 && type == 'F') {
687                 // We found the first file handle. Remember it's system_handle object type
688                 // and then use it to filter the following system_handle.
689                 // For some reason OB_TYPE_FILE should be 0x1A but empirically I find it
690                 // to be 0x1C, so we just make this test more dynamic.
691                 ob_type_file = sysh.bObjectType;
692             }
693 
694             // Try to get a filename out of that file or directory handle.
695             CString name("<unknown>");
696             bool ok = getFileName(remoteH, &name);
697 
698             if (gIsDebug) {
699                 fprintf(stderr, "P:%08x | t:%02x | f:%02x | v:%08x | %c | %s %s\n",
700                     sysh.dIdProcess, sysh.bObjectType, sysh.bFlags, sysh.wValue,
701                     type,
702                     ok ? "OK" : "FAIL",
703                     name.cstr()
704                     );
705             }
706 
707             if (ok) {
708                 // We got a file path. Let's check if it matches our target path.
709                 if (_strnicmp(searchPath, name.cstr(), searchPathLen) == 0) {
710                     // Remember this process id so that we can ignore all its following entries.
711                     matchProcessId = remoteId;
712 
713                     // Find out its process name
714                     CString procName("<unknown>");
715                     ok = getProcessName(processesPtr, remoteProcessId, &procName);
716                     if (ok) {
717                         numProcessMatch++;
718 
719                         if (!outModule->isEmpty()) {
720                             outModule->add(";");
721                         }
722                         outModule->add(procName.cstr());
723                         result = true;
724                     }
725 
726                     if (gIsDebug) {
727                         fprintf(stderr, "==> MATCH FOUND: %s  %s\n",
728                             ok ? "OK" : "FAIL",
729                             procName.cstr()
730                             );
731                     }
732                 }
733             }
734 
735         }
736 
737         if (remoteH != NULL) {
738             CloseHandle(remoteH);
739             remoteH = NULL;
740         }
741     }
742 
743 bail_out:
744 
745     if (gIsDebug) {
746         fprintf(stderr, "Processes matched: %d\n", numProcessMatch);
747         fprintf(stderr, "Handles: %d found, %d checked, %d dirs, %d files\n",
748                numHandleFound,
749                numHandleChecked,
750                numHandleDirs,
751                numHandleFiles);
752     }
753 
754     if (error != NULL) {
755         CString msg;
756         msg.setLastWin32Error(NULL);
757         if (gIsDebug) fprintf(stderr, "[ERROR] %s: %s", error, msg.cstr());
758     }
759 
760     if (remoteProcessH != NULL) {
761         CloseHandle(remoteProcessH);
762     }
763 
764     if (currProcessH != NULL) {
765         CloseHandle(currProcessH);
766     }
767 
768     if (handlesPtr != NULL) {
769         VirtualFree(handlesPtr, 0, MEM_RELEASE);
770         handlesPtr = NULL;
771     }
772 
773     terminate();
774 
775     return result;
776 }
777 
778 #endif /* _WIN32 */
779