// poolsnap.c // this program takes a snapshot of all the kernel pool tags. // and appends it to the logfile (arg) // pmon was model for this /* includes */ #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(POOLSNAP_INCLUDED) #define SORTLOG_INCLUDED #define ANALOG_INCLUDED #include "analog.c" #include "sortlog.c" #endif #include "tags.c" // // declarations // int __cdecl ulcomp(const void *e1,const void *e2); NTSTATUS QueryPoolTagInformationIterative( PUCHAR *CurrentBuffer, size_t *CurrentBufferSize ); // // definitions // #define NONPAGED 0 #define PAGED 1 #define BOTH 2 // // Printf format string for pool tag info. // #ifdef _WIN64 #define POOLTAG_PRINT_FORMAT " %4s %5s %18I64d %18I64d %16I64d %14I64d %12I64d\n" #else #define POOLTAG_PRINT_FORMAT " %4s %5s %9ld %9ld %8ld %7ld %6ld\n" #endif // from poolmon // raw input PSYSTEM_POOLTAG_INFORMATION PoolInfo; // // the amount of memory to increase the size // of the buffer for NtQuerySystemInformation at each step // #define BUFFER_SIZE_STEP 65536 // // the buffer used for NtQuerySystemInformation // PUCHAR CurrentBuffer = NULL; // // the size of the buffer used for NtQuerySystemInformation // size_t CurrentBufferSize = 0; // // formatted output // typedef struct _POOLMON_OUT { union { UCHAR Tag[4]; ULONG TagUlong; }; UCHAR NullByte; BOOL Changed; ULONG Type; SIZE_T Allocs[2]; SIZE_T AllocsDiff[2]; SIZE_T Frees[2]; SIZE_T FreesDiff[2]; SIZE_T Allocs_Frees[2]; SIZE_T Used[2]; SIZE_T UsedDiff[2]; SIZE_T Each[2]; } POOLMON_OUT, *PPOOLMON_OUT; PPOOLMON_OUT OutBuffer; PPOOLMON_OUT Out; UCHAR *PoolType[] = { "Nonp ", "Paged"}; VOID PoolsnapUsage(VOID) { printf("poolsnap [-?] [-t] []\n"); printf("poolsnap logs system pool usage to \n"); printf(" = poolsnap.log by default\n"); printf("-? Gives this help\n"); printf("-a Analyze the log file for leaks.\n"); printf("-t Output extra tagged information\n"); exit(-1); } #if !defined(POOLSNAP_INCLUDED) VOID AnalyzeLog ( PCHAR FileName, BOOL HtmlOutput ) { char * Args[4]; UNREFERENCED_PARAMETER(HtmlOutput); Args[0] = "memsnap.exe"; Args[1] = FileName; Args[2] = "_memsnap_temp_"; Args[3] = NULL; SortlogMain (3, Args); Args[0] = "memsnap.exe"; Args[1] = "-d"; Args[2] = "_memsnap_temp_"; Args[3] = NULL; AnalogMain (3, Args); DeleteFile ("_memsnap_temp_"); } #endif /* * FUNCTION: Main * * ARGUMENTS: See Usage * * RETURNS: 0 * */ #if defined(POOLSNAP_INCLUDED) int __cdecl PoolsnapMain (int argc, char* argv[]) #else int __cdecl main (int argc, char* argv[]) #endif { NTSTATUS Status; // status from NT api FILE* LogFile= NULL; // log file handle DWORD x= 0; // counter SIZE_T NumberOfPoolTags; INT iCmdIndex; // index into argv BOOL bOutputTags= FALSE; // if true, output standard tags // get higher priority in case system is bogged down if ( GetPriorityClass(GetCurrentProcess()) == NORMAL_PRIORITY_CLASS) { SetPriorityClass(GetCurrentProcess(),HIGH_PRIORITY_CLASS); } // // parse command line arguments // for( iCmdIndex=1; iCmdIndex < argc; iCmdIndex++ ) { CHAR chr; chr= *argv[iCmdIndex]; if( (chr=='-') || (chr=='/') ) { chr= argv[iCmdIndex][1]; switch( chr ) { case '?': PoolsnapUsage(); break; case 't': case 'T': bOutputTags= TRUE; break; case 'a': case 'A': if (argv[iCmdIndex + 1] != NULL) { AnalyzeLog (argv[iCmdIndex + 1], FALSE); } else { AnalyzeLog ("poolsnap.log", FALSE); } exit (0); default: printf("Invalid switch: %s\n",argv[iCmdIndex]); PoolsnapUsage(); break; } } else { if( LogFile ) { printf("Error: more than one file specified: %s\n",argv[iCmdIndex]); return(0); } LogFile= fopen(argv[iCmdIndex],"a"); if( !LogFile ) { printf("Error: Opening file %s\n",argv[iCmdIndex]); return(0); } } } // // if no file specified, use default name // if( !LogFile ) { if( (LogFile = fopen("poolsnap.log","a")) == NULL ) { printf("Error: opening file poolsnap.log\n"); return(0); } } // // print file header once // if( _filelength(_fileno(LogFile)) == 0 ) { fprintf(LogFile," Tag Type Allocs Frees Diff Bytes Per Alloc\n"); } fprintf(LogFile,"\n"); if( bOutputTags ) { OutputStdTags(LogFile, "poolsnap" ); } // grab all pool information // log line format, fixed column format Status = QueryPoolTagInformationIterative( &CurrentBuffer, &CurrentBufferSize ); if (! NT_SUCCESS(Status)) { printf("Failed to query pool tags information (status %08X). \n", Status); printf("Please check if pool tags are enabled. \n"); return (0); } PoolInfo = (PSYSTEM_POOLTAG_INFORMATION)CurrentBuffer; // // Allocate the output buffer. // OutBuffer = malloc (PoolInfo->Count * sizeof(POOLMON_OUT)); if (OutBuffer == NULL) { printf ("Error: cannot allocate internal buffer of %p bytes \n", (PVOID)(PoolInfo->Count * sizeof(POOLMON_OUT))); return (0); } Out = OutBuffer; if( NT_SUCCESS(Status) ) { for (x = 0; x < (int)PoolInfo->Count; x++) { // get pool info from buffer Out->Type = 0; // non-paged if (PoolInfo->TagInfo[x].NonPagedAllocs != 0) { Out->Allocs[NONPAGED] = PoolInfo->TagInfo[x].NonPagedAllocs; Out->Frees[NONPAGED] = PoolInfo->TagInfo[x].NonPagedFrees; Out->Used[NONPAGED] = PoolInfo->TagInfo[x].NonPagedUsed; Out->Allocs_Frees[NONPAGED] = PoolInfo->TagInfo[x].NonPagedAllocs - PoolInfo->TagInfo[x].NonPagedFrees; Out->TagUlong = PoolInfo->TagInfo[x].TagUlong; Out->Type |= (1 << NONPAGED); Out->Changed = FALSE; Out->NullByte = '\0'; Out->Each[NONPAGED] = Out->Used[NONPAGED] / (Out->Allocs_Frees[NONPAGED]?Out->Allocs_Frees[NONPAGED]:1); } // paged if (PoolInfo->TagInfo[x].PagedAllocs != 0) { Out->Allocs[PAGED] = PoolInfo->TagInfo[x].PagedAllocs; Out->Frees[PAGED] = PoolInfo->TagInfo[x].PagedFrees; Out->Used[PAGED] = PoolInfo->TagInfo[x].PagedUsed; Out->Allocs_Frees[PAGED] = PoolInfo->TagInfo[x].PagedAllocs - PoolInfo->TagInfo[x].PagedFrees; Out->TagUlong = PoolInfo->TagInfo[x].TagUlong; Out->Type |= (1 << PAGED); Out->Changed = FALSE; Out->NullByte = '\0'; Out->Each[PAGED] = Out->Used[PAGED] / (Out->Allocs_Frees[PAGED]?Out->Allocs_Frees[PAGED]:1); } Out += 1; } } else { fprintf(LogFile, "Query pooltags Failed %lx\n",Status); fprintf(LogFile, " Be sure to turn on 'enable pool tagging' in gflags and reboot.\n"); if( bOutputTags ) { fprintf(LogFile, "!Error:Query pooltags failed %lx\n",Status); fprintf(LogFile, "!Error: Be sure to turn on 'enable pool tagging' in gflags and reboot.\n"); } // If there is an operator around, wake him up, but keep moving Beep(1000,350); Beep(500,350); Beep(1000,350); exit(0); } // // sort by tag value which is big endian // NumberOfPoolTags = Out - OutBuffer; qsort((void *)OutBuffer, (size_t)NumberOfPoolTags, (size_t)sizeof(POOLMON_OUT), ulcomp); // // print in file // for (x = 0; x < (int)PoolInfo->Count; x++) { if ((OutBuffer[x].Type & (1 << NONPAGED))) { fprintf(LogFile, POOLTAG_PRINT_FORMAT, OutBuffer[x].Tag, PoolType[NONPAGED], OutBuffer[x].Allocs[NONPAGED], OutBuffer[x].Frees[NONPAGED], OutBuffer[x].Allocs_Frees[NONPAGED], OutBuffer[x].Used[NONPAGED], OutBuffer[x].Each[NONPAGED]); } if ((OutBuffer[x].Type & (1 << PAGED))) { fprintf(LogFile, POOLTAG_PRINT_FORMAT, OutBuffer[x].Tag, PoolType[PAGED], OutBuffer[x].Allocs[PAGED], OutBuffer[x].Frees[PAGED], OutBuffer[x].Allocs_Frees[PAGED], OutBuffer[x].Used[PAGED], OutBuffer[x].Each[PAGED]); } } // close file fclose(LogFile); return 0; } // comparison function for qsort // Tags are big endian int __cdecl ulcomp(const void *e1,const void *e2) { ULONG u1; u1 = ((PUCHAR)e1)[0] - ((PUCHAR)e2)[0]; if (u1 != 0) { return u1; } u1 = ((PUCHAR)e1)[1] - ((PUCHAR)e2)[1]; if (u1 != 0) { return u1; } u1 = ((PUCHAR)e1)[2] - ((PUCHAR)e2)[2]; if (u1 != 0) { return u1; } u1 = ((PUCHAR)e1)[3] - ((PUCHAR)e2)[3]; return u1; } /* * FUNCTION: * * QueryPoolTagInformationIterative * * ARGUMENTS: * * CurrentBuffer - a pointer to the buffer currently used for * NtQuerySystemInformation( SystemPoolTagInformation ). * It will be allocated if NULL or its size grown * if necessary. * * CurrentBufferSize - a pointer to a variable that holds the current * size of the buffer. * * * RETURNS: * * NTSTATUS returned by NtQuerySystemInformation or * STATUS_INSUFFICIENT_RESOURCES if the buffer must grow and the * heap allocation for it fails. * */ NTSTATUS QueryPoolTagInformationIterative( PUCHAR *CurrentBuffer, size_t *CurrentBufferSize ) { size_t NewBufferSize; NTSTATUS ReturnedStatus = STATUS_SUCCESS; if( CurrentBuffer == NULL || CurrentBufferSize == NULL ) { return STATUS_INVALID_PARAMETER; } if( *CurrentBufferSize == 0 || *CurrentBuffer == NULL ) { // // there is no buffer allocated yet // NewBufferSize = sizeof( UCHAR ) * BUFFER_SIZE_STEP; *CurrentBuffer = (PUCHAR) malloc( NewBufferSize ); if( *CurrentBuffer != NULL ) { *CurrentBufferSize = NewBufferSize; } else { // // insufficient memory // ReturnedStatus = STATUS_INSUFFICIENT_RESOURCES; } } // // iterate by buffer's size // while( *CurrentBuffer != NULL ) { ReturnedStatus = NtQuerySystemInformation ( SystemPoolTagInformation, *CurrentBuffer, (ULONG)*CurrentBufferSize, NULL ); if( ! NT_SUCCESS(ReturnedStatus) ) { // // free the current buffer // free( *CurrentBuffer ); *CurrentBuffer = NULL; if (ReturnedStatus == STATUS_INFO_LENGTH_MISMATCH) { // // try with a greater buffer size // NewBufferSize = *CurrentBufferSize + BUFFER_SIZE_STEP; *CurrentBuffer = (PUCHAR) malloc( NewBufferSize ); if( *CurrentBuffer != NULL ) { // // allocated new buffer // *CurrentBufferSize = NewBufferSize; } else { // // insufficient memory // ReturnedStatus = STATUS_INSUFFICIENT_RESOURCES; *CurrentBufferSize = 0; } } else { *CurrentBufferSize = 0; } } else { // // NtQuerySystemInformation returned success // break; } } return ReturnedStatus; }