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. 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 179 static void terminate() { 180 sNtQuerySystemInformationFunc = NULL; 181 sNtQueryInformationFileFunc = NULL; 182 sNtQueryObjectFunc = NULL; 183 } 184 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 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 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 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 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. 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. 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. 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 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 int 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