Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

813 lines
19 KiB

  1. /*++
  2. Module Name:
  3. common.c
  4. Abstract:
  5. This module contains common apis used by tlist & kill.
  6. --*/
  7. #include <windows.h>
  8. #include <winperf.h> // for Windows NT
  9. #include <tchar.h>
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include "common.h"
  14. #include "inetdbgp.h"
  15. //
  16. // manifest constants
  17. //
  18. #define INITIAL_SIZE 51200
  19. #define EXTEND_SIZE 25600
  20. #define REGKEY_PERF _T("software\\microsoft\\windows nt\\currentversion\\perflib")
  21. #define REGSUBKEY_COUNTERS _T("Counters")
  22. #define PROCESS_COUNTER _T("process")
  23. #define PROCESSID_COUNTER _T("id process")
  24. #define TITLE_SIZE 64
  25. typedef struct _SearchWin {
  26. LPCTSTR pExeName;
  27. LPDWORD pdwPid ;
  28. HWND* phwnd;
  29. } SearchWin ;
  30. typedef struct _SearchMod {
  31. LPSTR pExeName;
  32. LPBOOL pfFound;
  33. } SearchMod ;
  34. //
  35. // prototypes
  36. //
  37. BOOL CALLBACK
  38. EnumWindowsProc2(
  39. HWND hwnd,
  40. LPARAM lParam
  41. );
  42. HRESULT
  43. IsDllInProcess(
  44. DWORD dwProcessId,
  45. LPSTR pszName,
  46. LPBOOL pfFound
  47. );
  48. //
  49. // Functions
  50. //
  51. HRESULT
  52. KillTask(
  53. LPTSTR pName,
  54. LPSTR pszMandatoryModule
  55. )
  56. /*++
  57. Routine Description:
  58. Provides an API for killing a task.
  59. Arguments:
  60. pName - process name to look for
  61. pszMandatoryModule - if non NULL then this module must be loaded in the process space
  62. for it to be killed.
  63. Return Value:
  64. Status
  65. --*/
  66. {
  67. DWORD rc;
  68. TCHAR szSubKey[1024];
  69. LANGID lid;
  70. HKEY hKeyNames = NULL;
  71. DWORD dwType = 0;
  72. DWORD dwSize = 0;
  73. DWORD dwSpaceLeft = 0;
  74. LPBYTE buf = NULL;
  75. LPTSTR p;
  76. LPTSTR p2;
  77. PPERF_DATA_BLOCK pPerf;
  78. PPERF_OBJECT_TYPE pObj;
  79. PPERF_INSTANCE_DEFINITION pInst;
  80. PPERF_COUNTER_BLOCK pCounter;
  81. PPERF_COUNTER_DEFINITION pCounterDef;
  82. DWORD i;
  83. DWORD dwProcessIdTitle = 0;
  84. DWORD dwProcessIdCounter = 0;
  85. HRESULT hres = S_OK;
  86. HRESULT hresTemp = S_OK;
  87. HRESULT hresKill = S_OK;
  88. //
  89. // Look for the list of counters. Always use the neutral
  90. // English version, regardless of the local language. We
  91. // are looking for some particular keys, and we are always
  92. // going to do our looking in English. We are not going
  93. // to show the user the counter names, so there is no need
  94. // to go find the corresponding name in the local language.
  95. //
  96. lid = MAKELANGID( LANG_ENGLISH, SUBLANG_NEUTRAL );
  97. // There will be enough space in szSubKey to take the perf key
  98. // along with the lid that has come in.
  99. wsprintf( szSubKey, _T("%s\\%03x"), REGKEY_PERF, lid );
  100. rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  101. szSubKey,
  102. 0,
  103. KEY_READ,
  104. &hKeyNames
  105. );
  106. if (rc != ERROR_SUCCESS)
  107. {
  108. hres = HRESULT_FROM_WIN32( rc );
  109. goto exit;
  110. }
  111. //
  112. // get the buffer size for the counter names
  113. //
  114. rc = RegQueryValueEx( hKeyNames,
  115. REGSUBKEY_COUNTERS,
  116. NULL,
  117. &dwType,
  118. NULL,
  119. &dwSize
  120. );
  121. if (rc != ERROR_SUCCESS )
  122. {
  123. hres = HRESULT_FROM_WIN32( rc );
  124. goto exit;
  125. }
  126. //
  127. // allocate the counter names buffer
  128. //
  129. buf = (LPBYTE) malloc( dwSize );
  130. if (buf == NULL)
  131. {
  132. hres = HRESULT_FROM_WIN32( GetLastError() );
  133. goto exit;
  134. }
  135. memset( buf, 0, dwSize );
  136. //
  137. // read the counter names from the registry
  138. //
  139. rc = RegQueryValueEx( hKeyNames,
  140. REGSUBKEY_COUNTERS,
  141. NULL,
  142. &dwType,
  143. buf,
  144. &dwSize
  145. );
  146. if (rc != ERROR_SUCCESS)
  147. {
  148. hres = HRESULT_FROM_WIN32( GetLastError() );
  149. goto exit;
  150. }
  151. //
  152. // now loop thru the counter names looking for the following counters:
  153. //
  154. // 1. "Process" process name
  155. // 2. "ID Process" process id
  156. //
  157. // the buffer contains multiple null terminated strings and then
  158. // finally null terminated at the end. the strings are in pairs of
  159. // counter number and counter name.
  160. //
  161. p = (LPTSTR)buf;
  162. while (*p)
  163. {
  164. #pragma prefast(push)
  165. #pragma prefast(disable:400, "Don't complain about case insensitive compares")
  166. if (lstrcmpi(p, PROCESS_COUNTER) == 0)
  167. {
  168. //
  169. // look backwards for the counter number
  170. //
  171. // buffer should of advanced far enough
  172. // to have space before it now.
  173. if ( ( LPVOID )p < ( LPVOID )(buf+2) )
  174. {
  175. hres = E_FAIL;
  176. goto exit;
  177. }
  178. // szSubkey is 1024 characters of space.
  179. // we will be copying in a number some space
  180. // and then the word "process", this should
  181. // be plenty of space.
  182. //
  183. for( p2=p-2; _istdigit(*p2); p2--)
  184. {
  185. if ( ( LPVOID )p2 == ( LPVOID )buf )
  186. {
  187. hres = E_FAIL;
  188. goto exit;
  189. }
  190. }
  191. lstrcpy( szSubKey, p2+1 );
  192. }
  193. else if (lstrcmpi(p, PROCESSID_COUNTER) == 0)
  194. {
  195. // buffer should of advanced far enough
  196. // to have space before it now.
  197. if ( ( LPVOID )p < ( LPVOID )(buf+2) )
  198. {
  199. hres = E_FAIL;
  200. goto exit;
  201. }
  202. //
  203. // look backwards for the counter number
  204. //
  205. for( p2=p-2; _istdigit(*p2); p2--)
  206. {
  207. if ( ( LPVOID )p2 == ( LPVOID )buf )
  208. {
  209. hres = E_FAIL;
  210. goto exit;
  211. }
  212. }
  213. dwProcessIdTitle = _ttol( p2+1 );
  214. }
  215. #pragma prefast(pop)
  216. //
  217. // next string
  218. //
  219. p += (lstrlen(p) + 1);
  220. }
  221. //
  222. // free the counter names buffer
  223. //
  224. free( buf );
  225. //
  226. // allocate the initial buffer for the performance data
  227. //
  228. dwSize = INITIAL_SIZE;
  229. buf = malloc( dwSize );
  230. if (buf == NULL)
  231. {
  232. hres = HRESULT_FROM_WIN32( GetLastError() );
  233. goto exit;
  234. }
  235. memset( buf, 0, dwSize );
  236. for ( ; ; )
  237. {
  238. rc = RegQueryValueEx( HKEY_PERFORMANCE_DATA,
  239. szSubKey,
  240. NULL,
  241. &dwType,
  242. buf,
  243. &dwSize
  244. );
  245. pPerf = (PPERF_DATA_BLOCK) buf;
  246. //
  247. // check for success and valid perf data block signature
  248. //
  249. if ((rc == ERROR_SUCCESS) &&
  250. (dwSize >= sizeof(PERF_DATA_BLOCK)) &&
  251. (pPerf)->Signature[0] == (WCHAR)'P' &&
  252. (pPerf)->Signature[1] == (WCHAR)'E' &&
  253. (pPerf)->Signature[2] == (WCHAR)'R' &&
  254. (pPerf)->Signature[3] == (WCHAR)'F' )
  255. {
  256. break;
  257. }
  258. //
  259. // if buffer is not big enough, reallocate and try again
  260. //
  261. if (rc == ERROR_MORE_DATA)
  262. {
  263. dwSize += EXTEND_SIZE;
  264. free ( buf );
  265. buf = NULL;
  266. buf = malloc( dwSize );
  267. if (buf == NULL)
  268. {
  269. hres = HRESULT_FROM_WIN32( GetLastError() );
  270. goto exit;
  271. }
  272. memset( buf, 0, dwSize );
  273. }
  274. else
  275. {
  276. // in the off case that we got data back under
  277. // this key, but it was not the correct data, we
  278. // need to return some error.
  279. if ( rc == ERROR_SUCCESS )
  280. {
  281. rc = ERROR_INVALID_DATA;
  282. }
  283. goto exit;
  284. }
  285. }
  286. // make sure we don't ever walk past the end of the perf counter stuff.
  287. // Subtract the space the PERF_DATA_BLOCK takes
  288. dwSpaceLeft = dwSize - pPerf->HeaderLength;
  289. // Validate that pObj will still be pointing
  290. // to memory we just read.
  291. if ( dwSpaceLeft < sizeof(PERF_OBJECT_TYPE) )
  292. {
  293. rc = ERROR_INVALID_DATA;
  294. goto exit;
  295. }
  296. else
  297. {
  298. // Subtract the space the PERF_OBJECT_BLOCK takes
  299. dwSpaceLeft = dwSpaceLeft - sizeof(PERF_OBJECT_TYPE);
  300. }
  301. //
  302. // set the perf_object_type pointer
  303. //
  304. pObj = (PPERF_OBJECT_TYPE) ((LPBYTE)pPerf + pPerf->HeaderLength);
  305. //
  306. // loop thru the performance counter definition records looking
  307. // for the process id counter and then save its offset
  308. //
  309. // Validate that we have enough space for all the
  310. // counter definitions we are expecting.
  311. // to memory we just read.
  312. if ( dwSpaceLeft < sizeof(PERF_COUNTER_DEFINITION) * pObj->NumCounters )
  313. {
  314. rc = ERROR_INVALID_DATA;
  315. goto exit;
  316. }
  317. else
  318. {
  319. // Subtract the space the PERF_OBJECT_BLOCK takes
  320. dwSpaceLeft = dwSpaceLeft - ( sizeof(PERF_COUNTER_DEFINITION) * pObj->NumCounters ) ;
  321. }
  322. pCounterDef = (PPERF_COUNTER_DEFINITION) ((DWORD_PTR)pObj + pObj->HeaderLength);
  323. for (i=0; i<(DWORD)pObj->NumCounters; i++)
  324. {
  325. if (pCounterDef->CounterNameTitleIndex == dwProcessIdTitle)
  326. {
  327. dwProcessIdCounter = pCounterDef->CounterOffset;
  328. break;
  329. }
  330. pCounterDef++;
  331. }
  332. pInst = (PPERF_INSTANCE_DEFINITION) ((LPBYTE)pObj + pObj->DefinitionLength);
  333. //
  334. // loop thru the performance instance data extracting each process name
  335. // and process id
  336. //
  337. for (i=0; i<(DWORD)pObj->NumInstances; i++)
  338. {
  339. // Validate that we have enough space for the
  340. // instance definition.
  341. if ( dwSpaceLeft < sizeof(PERF_INSTANCE_DEFINITION) ||
  342. dwSpaceLeft < pInst->ByteLength )
  343. {
  344. rc = ERROR_INVALID_DATA;
  345. goto exit;
  346. }
  347. else
  348. {
  349. dwSpaceLeft = dwSpaceLeft - pInst->ByteLength;
  350. }
  351. //
  352. // pointer to the process name
  353. //
  354. p = (LPTSTR) ((LPBYTE)pInst + pInst->NameOffset);
  355. //
  356. // get the process id
  357. //
  358. pCounter = (PPERF_COUNTER_BLOCK) ((LPBYTE)pInst + pInst->ByteLength);
  359. // Validate that we have enough space for the
  360. // counter values we are expecting.
  361. if ( dwSpaceLeft < sizeof(PERF_COUNTER_BLOCK) ||
  362. dwSpaceLeft < pCounter->ByteLength )
  363. {
  364. rc = ERROR_INVALID_DATA;
  365. goto exit;
  366. }
  367. else
  368. {
  369. dwSpaceLeft = dwSpaceLeft - pCounter->ByteLength;
  370. }
  371. if ( lstrcmpi( p, pName ) == 0 )
  372. {
  373. //
  374. // Kill process now, do not update pTask array
  375. //
  376. BOOL fIsInProcess;
  377. DWORD dwProcessId = *((LPDWORD) ((LPBYTE)pCounter + dwProcessIdCounter));
  378. if ( pszMandatoryModule == NULL ||
  379. ( SUCCEEDED( hresTemp = IsDllInProcess( dwProcessId, pszMandatoryModule, &fIsInProcess ) ) &&
  380. fIsInProcess ) )
  381. {
  382. // OutputDebugStringW(L"Killing ");
  383. // OutputDebugStringW(pName);
  384. // OutputDebugStringW(L"\r\n");
  385. hresTemp = KillProcess( dwProcessId );
  386. }
  387. // Need to remember the first failure, but we want
  388. // to go on and try to kill the rest as well
  389. if ( FAILED ( hresTemp ) && SUCCEEDED( hresKill ) )
  390. {
  391. hresKill = hresTemp;
  392. }
  393. }
  394. //
  395. // next process
  396. //
  397. pInst = (PPERF_INSTANCE_DEFINITION) ((LPBYTE)pCounter + pCounter->ByteLength);
  398. }
  399. exit:
  400. if (buf)
  401. {
  402. free( buf );
  403. }
  404. if ( hKeyNames != NULL )
  405. {
  406. RegCloseKey( hKeyNames );
  407. }
  408. if ( SUCCEEDED ( hres ) )
  409. {
  410. return hresKill;
  411. }
  412. else
  413. {
  414. return hres;
  415. }
  416. }
  417. BOOL
  418. EnableDebugPrivNT(
  419. VOID
  420. )
  421. /*++
  422. Routine Description:
  423. Changes the process's privilege so that kill works properly.
  424. Arguments:
  425. Return Value:
  426. TRUE - success
  427. FALSE - failure
  428. --*/
  429. {
  430. HANDLE hToken;
  431. LUID DebugValue;
  432. TOKEN_PRIVILEGES tkp;
  433. //
  434. // Retrieve a handle of the access token
  435. //
  436. if (!OpenProcessToken(GetCurrentProcess(),
  437. TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
  438. &hToken))
  439. {
  440. return FALSE;
  441. }
  442. //
  443. // Enable the SE_DEBUG_NAME privilege
  444. //
  445. if (!LookupPrivilegeValue((LPTSTR) NULL,
  446. SE_DEBUG_NAME,
  447. &DebugValue))
  448. {
  449. return FALSE;
  450. }
  451. tkp.PrivilegeCount = 1;
  452. tkp.Privileges[0].Luid = DebugValue;
  453. tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  454. AdjustTokenPrivileges(hToken,
  455. FALSE,
  456. &tkp,
  457. sizeof(TOKEN_PRIVILEGES),
  458. (PTOKEN_PRIVILEGES) NULL,
  459. (PDWORD) NULL);
  460. //
  461. // The return value of AdjustTokenPrivileges can't be tested
  462. //
  463. if (GetLastError() != ERROR_SUCCESS)
  464. {
  465. return FALSE;
  466. }
  467. return TRUE;
  468. }
  469. BOOLEAN
  470. EnumModulesCallback(
  471. LPVOID pParam,
  472. PMODULE_INFO pModuleInfo
  473. )
  474. /*++
  475. Routine Description:
  476. Called by module enumerator with info on current module
  477. Arguments:
  478. pParam - as specified in the call to EnumModules()
  479. pModuleInfo - module information
  480. Return Value:
  481. TRUE to continue enumeration, FALSE to stop it
  482. --*/
  483. {
  484. if ( !_strcmpi( pModuleInfo->BaseName, ((SearchMod*)pParam)->pExeName ) )
  485. {
  486. *((SearchMod*)pParam)->pfFound = TRUE;
  487. return FALSE; // stop enumeration
  488. }
  489. return TRUE;
  490. }
  491. HRESULT
  492. IsDllInProcess(
  493. DWORD dwProcessId,
  494. LPSTR pszName,
  495. LPBOOL pfFound
  496. )
  497. /*++
  498. Routine Description:
  499. Check if a module ( e.g. DLL ) exists in specified process
  500. Arguments:
  501. dwProcessId - process ID to scan for module pszName
  502. pszName - module name to look for, e.g. "wam.dll"
  503. pfFound - updated with TRUE if pszName found in process dwProcessId
  504. valid only if functions succeed.
  505. Return Value:
  506. Status.
  507. --*/
  508. {
  509. HANDLE hProcess;
  510. HRESULT hres = S_OK;
  511. SearchMod sm;
  512. hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
  513. PROCESS_VM_READ,
  514. FALSE,
  515. dwProcessId );
  516. if ( hProcess == NULL )
  517. {
  518. // PID may have gone away while we were
  519. // working on it, if it has then we will
  520. // get invalid parameter when we try and
  521. // open it.
  522. if ( GetLastError() == ERROR_INVALID_PARAMETER )
  523. {
  524. *pfFound = FALSE;
  525. return S_OK;
  526. }
  527. return HRESULT_FROM_WIN32( GetLastError() );
  528. }
  529. sm.pExeName = pszName;
  530. sm.pfFound= pfFound;
  531. *pfFound = FALSE;
  532. if ( !EnumModules( hProcess, EnumModulesCallback, (LPVOID)&sm ) )
  533. {
  534. hres = E_FAIL;
  535. }
  536. CloseHandle( hProcess );
  537. return hres;
  538. }
  539. HRESULT
  540. KillProcess(
  541. DWORD dwPid
  542. )
  543. {
  544. HANDLE hProcess = NULL;
  545. HRESULT hres = S_OK;
  546. hProcess = OpenProcess( PROCESS_TERMINATE,
  547. FALSE,
  548. dwPid );
  549. if ( hProcess == NULL )
  550. {
  551. // Process might have gone away since we found it.
  552. if ( GetLastError() == ERROR_INVALID_PARAMETER )
  553. {
  554. return S_OK;
  555. }
  556. hres = HRESULT_FROM_WIN32( GetLastError() );
  557. }
  558. // OpenProcess worked
  559. if ( SUCCEEDED(hres) )
  560. {
  561. if (!TerminateProcess( hProcess, 1 ))
  562. {
  563. //
  564. // If error code is access denied then the process may have
  565. // all ready been terminated, so treat this as success. If
  566. // it was not caused by the process all ready being terminated
  567. // then we will catch the error below by timing out waiting
  568. // for the process to disappear.
  569. //
  570. if ( GetLastError() == ERROR_ACCESS_DENIED )
  571. {
  572. hres = S_OK;
  573. }
  574. else
  575. {
  576. hres = HRESULT_FROM_WIN32( GetLastError() );
  577. }
  578. }
  579. else
  580. {
  581. hres = S_OK;
  582. }
  583. CloseHandle( hProcess );
  584. }
  585. return hres;
  586. }
  587. VOID
  588. GetPidFromTitle(
  589. LPDWORD pdwPid,
  590. HWND* phwnd,
  591. LPCTSTR pExeName
  592. )
  593. /*++
  594. Routine Description:
  595. Callback function for window enumeration.
  596. Arguments:
  597. pdwPid - updated with process ID of window matching window name or 0 if window not found
  598. phwnd - updated with window handle matching searched window name
  599. pExeName - window name to look for. Only the # of char present in this name will be
  600. used during checking for a match ( e.g. "inetinfo.exe" will match "inetinfo.exe - Application error"
  601. Return Value:
  602. None. *pdwPid will be 0 if no match is found
  603. --*/
  604. {
  605. SearchWin sw;
  606. sw.pdwPid = pdwPid;
  607. sw.phwnd = phwnd;
  608. sw.pExeName = pExeName;
  609. *pdwPid = 0;
  610. //
  611. // enumerate all windows
  612. //
  613. EnumWindows( (WNDENUMPROC)EnumWindowsProc2, (LPARAM) &sw );
  614. }
  615. BOOL CALLBACK
  616. EnumWindowsProc2(
  617. HWND hwnd,
  618. LPARAM lParam
  619. )
  620. /*++
  621. Routine Description:
  622. Callback function for window enumeration.
  623. Arguments:
  624. hwnd - window handle
  625. lParam - ptr to SearchWin
  626. Return Value:
  627. TRUE - continues the enumeration
  628. --*/
  629. {
  630. DWORD pid = 0;
  631. TCHAR buf[TITLE_SIZE];
  632. SearchWin* psw = (SearchWin*)lParam;
  633. //
  634. // get the processid for this window
  635. //
  636. if (!GetWindowThreadProcessId( hwnd, &pid ))
  637. {
  638. return TRUE;
  639. }
  640. if (GetWindowText( hwnd, buf, sizeof(buf)/sizeof(TCHAR) ))
  641. {
  642. if ( lstrlen( buf ) > lstrlen( psw->pExeName ) )
  643. {
  644. buf[lstrlen( psw->pExeName )] = _T('\0');
  645. }
  646. if ( !lstrcmpi( psw->pExeName, buf ) )
  647. {
  648. *psw->phwnd = hwnd;
  649. *psw->pdwPid = pid;
  650. }
  651. }
  652. return TRUE;
  653. }