• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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