Source code of Windows XP (NT5)
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.

524 lines
11 KiB

  1. #define REASONABLE_NUMBER 8
  2. typedef BOOL (_ID_MATCH_FN)(
  3. PVOID A,
  4. PVOID B);
  5. typedef _ID_MATCH_FN * PID_MATCH_FN;
  6. typedef VOID (_ID_BANNER_FN)(
  7. PVOID Id
  8. );
  9. typedef _ID_BANNER_FN * PID_BANNER_FN;
  10. typedef VOID (_HANDLE_CALLBACK_FN)(
  11. PVOID Context,
  12. HANDLE Here,
  13. HANDLE There
  14. );
  15. typedef _HANDLE_CALLBACK_FN * PHANDLE_CALLBACK_FN;
  16. #define MATCH_LARGE_INT ((PID_MATCH_FN)1)
  17. typedef struct _HANDLE_TRACK {
  18. LIST_ENTRY List ;
  19. ULONG Flags ;
  20. ULONG Count ;
  21. ULONG Size ;
  22. PHANDLE Handles ;
  23. HANDLE HandleList[ REASONABLE_NUMBER ];
  24. UCHAR IdData[ 1 ];
  25. } HANDLE_TRACK, * PHANDLE_TRACK ;
  26. typedef struct _HANDLE_TRACK_ARRAY {
  27. ULONG Count ;
  28. ULONG Size ;
  29. ULONG IdDataSize ;
  30. PID_MATCH_FN MatchFunc ;
  31. LIST_ENTRY List ;
  32. } HANDLE_TRACK_ARRAY, * PHANDLE_TRACK_ARRAY ;
  33. typedef struct _THREAD_TRACK_INFO {
  34. CLIENT_ID Id ;
  35. PVOID Win32StartAddress ;
  36. DWORD Status ;
  37. } THREAD_TRACK_INFO ;
  38. typedef struct _HANDLE_LEAK_HELPER {
  39. PWSTR Type ;
  40. PID_BANNER_FN Banner ;
  41. PHANDLE_CALLBACK_FN Filter ;
  42. ULONG ArraySize ;
  43. PID_MATCH_FN Match ;
  44. } HANDLE_LEAK_HELPER, * PHANDLE_LEAK_HELPER ;
  45. _ID_BANNER_FN ThreadBanner ;
  46. _ID_BANNER_FN TokenBanner ;
  47. _HANDLE_CALLBACK_FN ThreadCallback ;
  48. _HANDLE_CALLBACK_FN TokenCallback ;
  49. HANDLE_LEAK_HELPER HandleLeakHelpers[] = {
  50. { L"Thread", ThreadBanner, ThreadCallback, sizeof( THREAD_TRACK_INFO ), MATCH_LARGE_INT },
  51. { L"Token", TokenBanner, TokenCallback, sizeof( TOKEN_CONTROL ), MATCH_LARGE_INT }
  52. };
  53. PHANDLE_TRACK_ARRAY
  54. CreateArray(
  55. ULONG IdDataSize,
  56. PID_MATCH_FN MatchFn
  57. )
  58. {
  59. PHANDLE_TRACK_ARRAY Array ;
  60. Array = RtlAllocateHeap( RtlProcessHeap(), 0, sizeof( HANDLE_TRACK_ARRAY ) );
  61. if ( Array )
  62. {
  63. Array->Count = 0 ;
  64. Array->Size = 0;
  65. Array->IdDataSize = IdDataSize ;
  66. Array->MatchFunc = MatchFn ;
  67. InitializeListHead( &Array->List );
  68. return Array ;
  69. }
  70. return NULL ;
  71. }
  72. VOID
  73. DeleteArray(
  74. PHANDLE_TRACK_ARRAY Array
  75. )
  76. {
  77. ULONG i ;
  78. PHANDLE_TRACK Track ;
  79. while ( !IsListEmpty( &Array->List ) )
  80. {
  81. Track = (PHANDLE_TRACK) RemoveHeadList( &Array->List );
  82. if ( Track->Handles != Track->HandleList )
  83. {
  84. RtlFreeHeap( RtlProcessHeap(), 0, Track->Handles );
  85. }
  86. RtlFreeHeap( RtlProcessHeap(), 0, Track );
  87. }
  88. RtlFreeHeap( RtlProcessHeap(), 0, Array );
  89. }
  90. VOID
  91. ExtendTrack(
  92. PHANDLE_TRACK Track
  93. )
  94. {
  95. PHANDLE NewHandle ;
  96. NewHandle = RtlAllocateHeap( RtlProcessHeap(), 0, (Track->Size + REASONABLE_NUMBER ) *
  97. sizeof( HANDLE ) );
  98. if ( NewHandle )
  99. {
  100. CopyMemory( NewHandle,
  101. Track->Handles,
  102. Track->Count * sizeof( HANDLE ) );
  103. if ( Track->Handles != Track->HandleList )
  104. {
  105. RtlFreeHeap( RtlProcessHeap(), 0, Track->Handles );
  106. }
  107. Track->Handles = NewHandle ;
  108. Track->Size += REASONABLE_NUMBER ;
  109. }
  110. }
  111. VOID
  112. AddHandleToArray(
  113. PHANDLE_TRACK_ARRAY Array,
  114. PVOID IdData,
  115. HANDLE Handle
  116. )
  117. {
  118. ULONG i;
  119. ULONG j;
  120. BOOL Match ;
  121. PHANDLE_TRACK Track ;
  122. PLIST_ENTRY Scan ;
  123. Scan = Array->List.Flink ;
  124. while ( Scan != &Array->List )
  125. {
  126. Track = (PHANDLE_TRACK) Scan ;
  127. if ( Array->MatchFunc == MATCH_LARGE_INT )
  128. {
  129. Match = ((PLARGE_INTEGER) Track->IdData)->QuadPart ==
  130. ((PLARGE_INTEGER) IdData)->QuadPart ;
  131. }
  132. else
  133. {
  134. Match = Array->MatchFunc( Track->IdData, IdData );
  135. }
  136. if ( Match )
  137. {
  138. //
  139. // We have a match:
  140. //
  141. if ( Track->Count == Track->Size )
  142. {
  143. ExtendTrack( Track );
  144. }
  145. if ( Track->Count < Track->Size )
  146. {
  147. Track->Handles[
  148. Track->Count ] = Handle ;
  149. Track->Count++;
  150. }
  151. return ;
  152. }
  153. Scan = Scan->Flink ;
  154. }
  155. //
  156. // No match, gotta add a new tid
  157. //
  158. Track = RtlAllocateHeap( RtlProcessHeap(), 0,
  159. sizeof( HANDLE_TRACK ) + Array->IdDataSize );
  160. if ( Track )
  161. {
  162. Track->Size = REASONABLE_NUMBER ;
  163. Track->Count = 1 ;
  164. Track->Handles = Track->HandleList ;
  165. Track->HandleList[0] = Handle ;
  166. CopyMemory( Track->IdData, IdData, Array->IdDataSize );
  167. InsertTailList( &Array->List, &Track->List );
  168. }
  169. }
  170. VOID
  171. DumpArray(
  172. PHANDLE_TRACK_ARRAY Array,
  173. PID_BANNER_FN Banner
  174. )
  175. {
  176. ULONG j;
  177. PHANDLE_TRACK Track ;
  178. PLIST_ENTRY Scan ;
  179. Scan = Array->List.Flink ;
  180. while ( Scan != &Array->List )
  181. {
  182. Track = (PHANDLE_TRACK) Scan ;
  183. Banner( Track->IdData );
  184. dprintf(" Handles \t%d: ", Track->Count );
  185. for ( j = 0 ; j < Track->Count ; j++ )
  186. {
  187. dprintf("%x, ", Track->Handles[j] );
  188. }
  189. dprintf("\n");
  190. Scan = Scan->Flink ;
  191. }
  192. }
  193. VOID
  194. HandleScanner(
  195. HANDLE hCurrentProcess,
  196. PWSTR Type,
  197. PVOID Context,
  198. PHANDLE_CALLBACK_FN Callback
  199. )
  200. {
  201. DWORD HandleCount;
  202. NTSTATUS Status;
  203. DWORD Total;
  204. DWORD Handle;
  205. DWORD Hits;
  206. DWORD Matches;
  207. HANDLE hHere ;
  208. PHANDLE_TRACK_ARRAY Array ;
  209. POBJECT_TYPE_INFORMATION pTypeInfo;
  210. UCHAR Buffer[1024];
  211. Status = NtQueryInformationProcess( hCurrentProcess,
  212. ProcessHandleCount,
  213. &HandleCount,
  214. sizeof( HandleCount ),
  215. NULL );
  216. if ( !NT_SUCCESS( Status ) )
  217. {
  218. return;
  219. }
  220. Hits = 0;
  221. Handle = 0;
  222. Matches = 0;
  223. while ( Hits < HandleCount )
  224. {
  225. Status = NtDuplicateObject( hCurrentProcess, (HANDLE)(DWORD_PTR)Handle,
  226. NtCurrentProcess(), &hHere,
  227. 0, 0,
  228. DUPLICATE_SAME_ACCESS );
  229. if ( NT_SUCCESS( Status ) )
  230. {
  231. pTypeInfo = (POBJECT_TYPE_INFORMATION) Buffer;
  232. ZeroMemory( Buffer, 1024 );
  233. Status = NtQueryObject( hHere, ObjectTypeInformation, pTypeInfo, 1024, NULL );
  234. if (NT_SUCCESS(Status))
  235. {
  236. if ( wcscmp( pTypeInfo->TypeName.Buffer, Type ) == 0 )
  237. {
  238. //
  239. // Score!
  240. //
  241. Callback( Context, hHere, (HANDLE)(DWORD_PTR)Handle );
  242. Matches++ ;
  243. }
  244. }
  245. Hits++ ;
  246. NtClose( hHere );
  247. }
  248. Handle += 4;
  249. }
  250. dprintf("%d handles to objects of type %ws\n", Matches, Type );
  251. }
  252. VOID
  253. ThreadBanner(
  254. PVOID Id
  255. )
  256. {
  257. UCHAR Symbol[ MAX_PATH ];
  258. DWORD_PTR Disp ;
  259. THREAD_TRACK_INFO * Info ;
  260. UCHAR ExitStatus[ 32 ];
  261. Info = (THREAD_TRACK_INFO *) Id ;
  262. Symbol[0] = '\0';
  263. GetSymbol( Info->Win32StartAddress, Symbol, &Disp );
  264. if ( Info->Status != STILL_ACTIVE )
  265. {
  266. sprintf(ExitStatus, " Stopped, %#x", Info->Status );
  267. }
  268. else
  269. {
  270. strcpy( ExitStatus, "<Running>");
  271. }
  272. if ( Symbol[0] )
  273. {
  274. dprintf("Thread %x.%x (%s) %s\n", Info->Id.UniqueProcess,
  275. Info->Id.UniqueThread,
  276. Symbol,
  277. ExitStatus );
  278. }
  279. else
  280. {
  281. dprintf("Thread %x.%x %s\n", Info->Id.UniqueProcess,
  282. Info->Id.UniqueThread,
  283. ExitStatus );
  284. }
  285. }
  286. VOID
  287. ThreadCallback(
  288. PVOID Context,
  289. HANDLE Here,
  290. HANDLE There
  291. )
  292. {
  293. NTSTATUS Status ;
  294. THREAD_BASIC_INFORMATION Info;
  295. THREAD_TRACK_INFO ThdInfo ;
  296. ZeroMemory( &ThdInfo, sizeof( ThdInfo ) );
  297. Status = NtQueryInformationThread( Here,
  298. ThreadBasicInformation,
  299. &Info,
  300. sizeof( Info ),
  301. NULL );
  302. if ( NT_SUCCESS( Status ) )
  303. {
  304. ThdInfo.Id = Info.ClientId ;
  305. ThdInfo.Status = Info.ExitStatus ;
  306. Status = NtQueryInformationThread( Here,
  307. ThreadQuerySetWin32StartAddress,
  308. &ThdInfo.Win32StartAddress,
  309. sizeof( PVOID ),
  310. NULL );
  311. AddHandleToArray( Context, &ThdInfo , There );
  312. }
  313. }
  314. VOID
  315. TokenCallback(
  316. PVOID Context,
  317. HANDLE Here,
  318. HANDLE There
  319. )
  320. {
  321. NTSTATUS Status ;
  322. TOKEN_CONTROL Control ;
  323. TOKEN_STATISTICS Stats ;
  324. ULONG Size ;
  325. Status = NtQueryInformationToken( Here,
  326. TokenStatistics,
  327. &Stats,
  328. sizeof( Stats ),
  329. &Size );
  330. if ( NT_SUCCESS( Status ) )
  331. {
  332. Control.TokenId = Stats.TokenId ;
  333. Control.AuthenticationId = Stats.AuthenticationId ;
  334. Control.ModifiedId = Stats.ModifiedId ;
  335. NtQueryInformationToken( Here, TokenSource, &Control.TokenSource, sizeof( TOKEN_SOURCE ), &Size );
  336. AddHandleToArray( Context, &Control, There );
  337. }
  338. else
  339. {
  340. dprintf("Unable to query token information, %x\n", Status );
  341. }
  342. }
  343. VOID
  344. TokenBanner(
  345. PVOID Id
  346. )
  347. {
  348. PTOKEN_CONTROL Control ;
  349. Control = (PTOKEN_CONTROL) Id ;
  350. dprintf("Token Id %x:%x, LogonId = %x:%x, Source = %s\n",
  351. Control->TokenId.HighPart, Control->TokenId.LowPart,
  352. Control->AuthenticationId.HighPart, Control->AuthenticationId.LowPart,
  353. Control->TokenSource.SourceName );
  354. }
  355. DECLARE_API( hleak )
  356. {
  357. UNICODE_STRING String ;
  358. PHANDLE_LEAK_HELPER Helper = NULL ;
  359. PHANDLE_TRACK_ARRAY Array ;
  360. int i ;
  361. INIT_API();
  362. if ( !args ||
  363. (*args == '\0' ) )
  364. {
  365. dprintf( "!hleak <typename>\n" );
  366. goto Exit;
  367. }
  368. while ( *args == ' ' )
  369. {
  370. args++ ;
  371. }
  372. if ( !RtlCreateUnicodeStringFromAsciiz( &String, args ) )
  373. {
  374. goto Exit;
  375. }
  376. for ( i = 0 ;
  377. i < sizeof( HandleLeakHelpers ) / sizeof( HANDLE_LEAK_HELPER ) ;
  378. i++ )
  379. {
  380. if ( _wcsicmp( String.Buffer, HandleLeakHelpers[ i ].Type ) == 0 )
  381. {
  382. Helper = &HandleLeakHelpers[ i ];
  383. break;
  384. }
  385. }
  386. if ( Helper == NULL )
  387. {
  388. dprintf( "The type '%ws' was not recognized. Valid types are:\n", String.Buffer );
  389. for ( i = 0 ;
  390. i < sizeof( HandleLeakHelpers ) / sizeof( HANDLE_LEAK_HELPER ) ;
  391. i++ )
  392. {
  393. dprintf( "\t%ws\n", HandleLeakHelpers[ i ].Type );
  394. }
  395. RtlFreeUnicodeString( &String );
  396. goto Exit;
  397. }
  398. RtlFreeUnicodeString( &String );
  399. Array = CreateArray( Helper->ArraySize, Helper->Match );
  400. if ( !Array )
  401. {
  402. dprintf( "not enough memory\n" );
  403. }
  404. HandleScanner( g_hCurrentProcess,
  405. Helper->Type,
  406. Array,
  407. Helper->Filter );
  408. DumpArray( Array, Helper->Banner );
  409. DeleteArray( Array );
  410. Exit:
  411. EXIT_API();
  412. }