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