/*++ Copyright (c) 1998 Microsoft Corporation Module Name: regbin.c Abstract: This module contains functions to check bin header and bin body consistency. Author: Dragos C. Sambotin (dragoss) 30-Dec-1998 Revision History: --*/ #include "chkreg.h" extern ULONG TotalKeyNode; extern ULONG TotalKeyValue; extern ULONG TotalKeyIndex; extern ULONG TotalKeySecurity; extern ULONG TotalValueIndex; extern ULONG TotalUnknown; extern ULONG CountKeyNode; extern ULONG CountKeyValue; extern ULONG CountKeyIndex; extern ULONG CountKeySecurity; extern ULONG CountValueIndex; extern ULONG CountUnknown; extern ULONG TotalFree; extern ULONG FreeCount; extern ULONG TotalUsed; extern PUCHAR Base; extern FILE *OutputFile; extern HCELL_INDEX RootCell; extern PHBIN FirstBin; extern PHBIN MaxBin; extern ULONG HiveLength; extern LONG BinIndex; extern BOOLEAN FixHive; extern BOOLEAN SpaceUsage; extern BOOLEAN CompactHive; ULONG BinFreeDisplaySize[HHIVE_FREE_DISPLAY_SIZE]; ULONG BinFreeDisplayCount[HHIVE_FREE_DISPLAY_SIZE]; ULONG FreeDisplaySize[HHIVE_FREE_DISPLAY_SIZE]; ULONG FreeDisplayCount[HHIVE_FREE_DISPLAY_SIZE]; ULONG BinUsedDisplaySize[HHIVE_FREE_DISPLAY_SIZE]; ULONG BinUsedDisplayCount[HHIVE_FREE_DISPLAY_SIZE]; ULONG UsedDisplaySize[HHIVE_FREE_DISPLAY_SIZE]; ULONG UsedDisplayCount[HHIVE_FREE_DISPLAY_SIZE]; BOOLEAN ChkAllocatedCell(HCELL_INDEX Cell); CCHAR ChkRegFindFirstSetLeft[256] = { 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}; #define ComputeFreeIndex(Index, Size) \ { \ Index = (Size >> HHIVE_FREE_DISPLAY_SHIFT) - 1; \ if (Index >= HHIVE_LINEAR_INDEX ) { \ \ /* \ ** Too big for the linear lists, compute the exponential \ ** list. \ */ \ \ if (Index > 255) { \ /* \ ** Too big for all the lists, use the last index. \ */ \ Index = HHIVE_FREE_DISPLAY_SIZE-1; \ } else { \ Index = ChkRegFindFirstSetLeft[Index] + \ HHIVE_FREE_DISPLAY_BIAS; \ } \ } \ } BOOLEAN ChkBinHeader(PHBIN Bin, ULONG FileOffset, ULONG Index ) /*++ Routine Description: Checks the validity of the Bin header. The following tests are done: 1. the Size should not be bigger than the remaining of the file 2. the Size should not be smaller than HBLOCK_SIZE 3. the signature should be valid (HBIN_SIGNATURE) 4. the file offset should match the actual position in the hive file. Arguments: Bin - supplies a pointer to the bin to be checked. FileOffset - provides the actual pposition within the file Index - the index of the bin within the bin list of the hive Return Value: FALSE - the bin header is corrupted and was not fixed. Either this is a critical corruption, or the /R argument was not present in the command line. TRUE - The bin header is OK, or it was successfully recovered. --*/ { BOOLEAN bRez = TRUE; PHCELL p; p = (PHCELL)((PUCHAR)Bin + sizeof(HBIN)); if(Bin->Size > (HiveLength - FileOffset)) { bRez = FALSE; fprintf(stderr, "Size too big (%lu) in Bin header of Bin (%lu)\n",Bin->Size,Index); if(FixHive) { // // REPAIR: set the actual size to HiveLength-FileOffset // Bin->Size = HiveLength-FileOffset; p->Size = Bin->Size -sizeof(HBIN); bRez = TRUE; } else { if(CompactHive) { // any attempt to compact a corrupted hive will fail CompactHive = FALSE; fprintf(stderr, "Run chkreg /R to fix.\n"); } } } if((Bin->Size < HBLOCK_SIZE) || ((Bin->Size % HBLOCK_SIZE) != 0)) { bRez = FALSE; fprintf(stderr, "Size too small (%lu) in Bin header of Bin (%lu)\n",Bin->Size,Index); if(FixHive) { // // REPAIR: set the actual size to minimmum possible size HBLOCK_SIZE // Bin->Size = HBLOCK_SIZE; p->Size = Bin->Size -sizeof(HBIN); bRez = TRUE; } else { if(CompactHive) { // any attempt to compact a corrupted hive will fail CompactHive = FALSE; fprintf(stderr, "Run chkreg /R to fix.\n"); } } } if(Bin->Signature != HBIN_SIGNATURE) { bRez = FALSE; fprintf(stderr, "Invalid signature (%lx) in Bin header of Bin (%lu)\n",Bin->Signature,Index); if(FixHive) { // // REPAIR: reset the bin signature // Bin->Signature = HBIN_SIGNATURE; bRez = TRUE; } else { if(CompactHive) { // any attempt to compact a corrupted hive will fail CompactHive = FALSE; fprintf(stderr, "Run chkreg /R to fix.\n"); } } } if(Bin->FileOffset != FileOffset) { bRez = FALSE; fprintf(stderr, "Actual FileOffset [%lx] and Bin FileOffset [%lx] do not match in Bin (%lu); Size = (%lx)\n",FileOffset,Bin->FileOffset,Index,Bin->Size); if(FixHive) { // // REPAIR: reset the bin FileOffset // Bin->FileOffset = FileOffset; bRez = TRUE; } else { if(CompactHive) { // any attempt to compact a corrupted hive will fail CompactHive = FALSE; fprintf(stderr, "Run chkreg /R to fix.\n"); } } } return bRez; } BOOLEAN ChkBin( PHBIN Bin, ULONG IndexBin, ULONG Starting, double *Rate ) /*++ Routine Description: Steps through all of the cells in the bin. Make sure that they are consistent with each other, and with the bin header. Compute the usage rate for the current bin. Add all used cells to the unknown list (candidates to lost cells). Compute the used space and allocated cells by cell signature. Compute the free space size and number of cells. Test the cell size for reasonable limits. A cell should be smaller than the containing bin and should not exceed the bin boundaries. A cell should fit in only one contiguos bin!!! Arguments: Bin - supplies a pointer to the bin to be checked. Index - the index of the bin within the bin list of the hive Starting - starting address of the in-memory hive representation. Rate - usage rate for this bin Return Value: FALSE - the bin is corrupted and was not fixed. Either this is a critical corruption, or the /R argument was not present in the command line. TRUE - The bin is OK, or it was successfully recovered. --*/ { ULONG freespace = 0L; ULONG allocated = 0L; BOOLEAN bRez = TRUE; HCELL_INDEX cellindex; PHCELL p; ULONG Size; ULONG Index; double TmpRate; p = (PHCELL)((PUCHAR)Bin + sizeof(HBIN)); while (p < (PHCELL)((PUCHAR)Bin + Bin->Size)) { cellindex = (HCELL_INDEX)((PUCHAR)p - Base); if (p->Size >= 0) { // // It is a free cell. // Size = (ULONG)p->Size; if ( (Size > Bin->Size) || ( (PHCELL)(Size + (PUCHAR)p) > (PHCELL)((PUCHAR)Bin + Bin->Size) ) ) { bRez = FALSE; fprintf(stderr, "Impossible cell size in free cell (%lu) in Bin header of Bin (%lu)\n",Size,IndexBin); if(FixHive) { // // REPAIR: set the cell size to the largest possible hereon (ie. Bin + Bin->Size - p ); reset the Size too!!! // bRez = TRUE; p->Size = (ULONG)((PUCHAR)Bin + Bin->Size - (PUCHAR)p); } else { if(CompactHive) { // any attempt to compact a corrupted hive will fail CompactHive = FALSE; fprintf(stderr, "Run chkreg /R to fix.\n"); } } } freespace += Size; TotalFree += Size; FreeCount++; if( SpaceUsage ) { // only if we are interested in the usage map // store the length of this free cell ComputeFreeIndex(Index, Size); BinFreeDisplaySize[Index] += Size; // and increment the count of free cells of this particular size BinFreeDisplayCount[Index]++; } }else{ // // It is used cell. Check for signature // UCHAR *C; USHORT Sig; int i,j; // All used cells are leak candidates AddCellToUnknownList(cellindex); Size = (ULONG)(p->Size * -1); if ( (Size > Bin->Size) || ( (PHCELL)(Size + (PUCHAR)p) > (PHCELL)((PUCHAR)Bin + Bin->Size) ) ) { bRez = FALSE; fprintf(stderr, "Impossible cell size in allocated cell (%lu) in Bin header of Bin (%lu)\n",Size,IndexBin); if(FixHive) { // // REPAIR: set the cell size to the largest possible hereon (ie. Bin + Bin->Size - p ); reset the Size too!!! // bRez = TRUE; p->Size = (LONG)((PUCHAR)Bin + Bin->Size - (PUCHAR)p); // it's a used cell, remember ? p->Size *= -1; } else { if(CompactHive) { // any attempt to compact a corrupted hive will fail CompactHive = FALSE; fprintf(stderr, "Run chkreg /R to fix.\n"); } } } allocated += Size; if( SpaceUsage ) { // only if we are interested in the usage map // store the length of this used cell ComputeFreeIndex(Index, Size); BinUsedDisplaySize[Index] += Size; // and increment the count of used cells of this particular size BinUsedDisplayCount[Index]++; } TotalUsed=TotalUsed+Size; C= (UCHAR *) &(p->u.NewCell.u.UserData); Sig=(USHORT) p->u.NewCell.u.UserData; switch(Sig){ case CM_LINK_NODE_SIGNATURE: printf("Link Node !\n"); TotalKeyNode=TotalKeyNode+Size; CountKeyNode++; break; case CM_KEY_NODE_SIGNATURE: { PCM_KEY_NODE Pcan; TotalKeyNode=TotalKeyNode+Size; CountKeyNode++; Pcan = (PCM_KEY_NODE)C; if(Pcan->ValueList.Count){ PHCELL TmpP; TmpP = (PHCELL) (Starting + Pcan->ValueList.List); TotalValueIndex=TotalValueIndex - TmpP->Size; CountValueIndex++; } } break; case CM_KEY_VALUE_SIGNATURE: TotalKeyValue=TotalKeyValue+Size; CountKeyValue++; break; case CM_KEY_FAST_LEAF: case CM_KEY_HASH_LEAF: case CM_KEY_INDEX_LEAF: case CM_KEY_INDEX_ROOT: TotalKeyIndex=TotalKeyIndex+Size; CountKeyIndex++; break; case CM_KEY_SECURITY_SIGNATURE: TotalKeySecurity=TotalKeySecurity+Size; CountKeySecurity++; break; default: // // No signature, it can be data or index cells. // Or there must be some registry leak here. // TotalUnknown=TotalUnknown+Size; CountUnknown++; break; } } p = (PHCELL)((PUCHAR)p + Size); } *Rate = TmpRate = (double)(((double)allocated)/((double)(allocated+freespace))); TmpRate *= 100.00; fprintf(OutputFile,"Bin [%5lu], usage %.2f%%\r",IndexBin,(float)TmpRate); return bRez; } BOOLEAN ChkPhysicalHive() /*++ Routine Description: Checks the integrity of the hive by stepping through all of the cells in the hive. Collects and displays statistics, according to the command line parameters. Arguments: None. Return Value: FALSE - the hive is corrupted and was not fixed. Either this is a critical corruption, or the /R argument was not present in the command line. TRUE - The hive is OK, or it was successfully recovered. --*/ { ULONG Starting; PHBIN Bin = FirstBin; LONG Index; ULONG FileOffset; double Rate,RateTotal = 0.0; BOOLEAN bRez = TRUE; int i; Starting=(ULONG) Bin; Index=0; FileOffset = 0; for(i=0;iSize; } Bin = (PHBIN)((ULONG)Bin + Bin->Size); Index++; } RateTotal *= 100.00; RateTotal /= (double)Index; fprintf(OutputFile,"Number of Bins in hive: %lu \n",Index); fprintf(OutputFile,"Total Hive space usage: %.2f%% \n",(float)RateTotal); if( SpaceUsage ) { // only if we are interested in the usage map if( BinIndex == -1 ) { // space usage display per entire hive fprintf(OutputFile,"\nHive Display Map: Free Cells, Free Size\t\t Used Cells, Used Size\n"); for(i=0;iSignature != HBASE_BLOCK_SIGNATURE) { fprintf(stderr, "Fatal: Invalid Base Block signature (0x%lx)",BaseBlock->Signature); bRez = FALSE; if(FixHive) { // // REPAIR: reset the signature // fprintf(stderr, " ... unable to fix"); } else { if(CompactHive) { // any attempt to compact a corrupted hive will fail CompactHive = FALSE; } } fprintf(stderr, "\n"); } if(BaseBlock->Major != HSYS_MAJOR) { bRez = FALSE; fprintf(stderr, "Fatal: Invalid hive file Major version (%lu)",BaseBlock->Major); if(FixHive) { // // Fatal: unable to fix this // fprintf(stderr, " ... unable to fix"); } else { if(CompactHive) { // any attempt to compact a corrupted hive will fail CompactHive = FALSE; } } fprintf(stderr, "\n"); } if(BaseBlock->Minor > HSYS_MINOR_SUPPORTED) { bRez = FALSE; fprintf(stderr, "Fatal: Invalid hive file Minor version (%lu)",BaseBlock->Minor); if(FixHive) { // // Fatal: unable to fix this // fprintf(stderr, " ... unable to fix"); } else { if(CompactHive) { // any attempt to compact a corrupted hive will fail CompactHive = FALSE; } } fprintf(stderr, "\n"); } if(BaseBlock->Format != HBASE_FORMAT_MEMORY) { bRez = FALSE; fprintf(stderr, "Fatal: Invalid hive memory format (%lu)",BaseBlock->Format); if(FixHive) { // // Fatal: unable to fix this // fprintf(stderr, " ... unable to fix"); } else { if(CompactHive) { // any attempt to compact a corrupted hive will fail CompactHive = FALSE; } } fprintf(stderr, "\n"); } if((BaseBlock->Length + HBLOCK_SIZE) > dwFileSize) { fprintf(stderr, "Fatal: Invalid Hive file Length (%lu)",BaseBlock->Length); bRez = FALSE; if(FixHive) { // // REPAIR: unable to fix this // fprintf(stderr, " ... unable to fix"); } else { if(CompactHive) { // any attempt to compact a corrupted hive will fail CompactHive = FALSE; } } fprintf(stderr, "\n"); } if(!bRez) { // // Fatal Base Block corruption; no point to continue. // return bRez; } if(BaseBlock->Sequence1 != BaseBlock->Sequence2) { fprintf(stderr, "Sequence numbers do not match (%lu,%lu)",BaseBlock->Sequence1,BaseBlock->Sequence2); bRez = FALSE; if(FixHive) { // // REPAIR: enforce Sequence2 to Sequence1 // bRez = TRUE; BaseBlock->Sequence2 = BaseBlock->Sequence1; fprintf(stderr, " ... fixed"); } else { if(CompactHive) { // any attempt to compact a corrupted hive will fail CompactHive = FALSE; fprintf(stderr, "\nRun chkreg /R to fix."); } } fprintf(stderr, "\n"); } CheckSum = ComputeHeaderCheckSum(BaseBlock); if(BaseBlock->CheckSum != CheckSum) { fprintf(stderr, "Invalid Base Block CheckSum (0x%lx)",BaseBlock->CheckSum); bRez = FALSE; if(FixHive) { // // REPAIR: reset the signature // bRez = TRUE; BaseBlock->CheckSum = CheckSum; fprintf(stderr, " ... fixed"); } else { if(CompactHive) { // any attempt to compact a corrupted hive will fail CompactHive = FALSE; fprintf(stderr, "\nRun chkreg /R to fix."); } } fprintf(stderr, "\n"); } return bRez; } BOOLEAN ChkSecurityDescriptors( ) /*++ Routine Description: Walks the list of security descriptors present in the hive and passes each security descriptor to RtlValidSecurityDescriptor. Also checks the validity of the FLink <==> BLink relationship between cells. Arguments: Return Value: TRUE - All security descriptors are valid FALSE - At least one security descriptor is invalid, and/or cannot be fixed --*/ { PCM_KEY_NODE RootNode; PCM_KEY_SECURITY SecurityCell; HCELL_INDEX ListAnchor; HCELL_INDEX NextCell; HCELL_INDEX LastCell; BOOLEAN bRez = TRUE; // check/fix the root cell (is allocated?) ChkAllocatedCell(RootCell); RootNode = (PCM_KEY_NODE) GetCell(RootCell); ListAnchor = NextCell = RootNode->Security; do { // is the next cell allocated? ChkAllocatedCell(NextCell); SecurityCell = (PCM_KEY_SECURITY) GetCell(NextCell); if (SecurityCell->Signature != CM_KEY_SECURITY_SIGNATURE) { bRez = FALSE; fprintf(stderr, "Fatal: Invalid signature (0x%lx) in Security cell 0x%lx ",SecurityCell->Signature,NextCell); if(FixHive) { // // REPAIR: // FATAL: Mismatched signature cannot be fixed. Unable to fix this. // fprintf(stderr, " ... unable to fix"); } else { if(CompactHive) { // any attempt to compact a corrupted hive will fail CompactHive = FALSE; } } fprintf(stderr, "\n"); return bRez; } if (NextCell != ListAnchor) { // // Check to make sure that our Blink points to where we just // came from. // if (SecurityCell->Blink != LastCell) { fprintf(stderr, "Invalid backward link in security cell (0x%lx)",NextCell); if(FixHive) { // // REPAIR: reset the link // SecurityCell->Blink = LastCell; fprintf(stderr, " ... fixed"); } else { bRez = FALSE; if(CompactHive) { // any attempt to compact a corrupted hive will fail CompactHive = FALSE; fprintf(stderr, "\nRun chkreg /R to fix."); } } fprintf(stderr, "\n"); } } if (!RtlValidSecurityDescriptor(&SecurityCell->Descriptor)) { bRez = FALSE; fprintf(stderr, "Invalid security descriptor in Security cell 0x%lx ",NextCell); if(FixHive) { // // REPAIR: remove the cell from the list and delete it! // PCM_KEY_SECURITY Before = (PCM_KEY_SECURITY) GetCell(SecurityCell->Blink); PCM_KEY_SECURITY After = (PCM_KEY_SECURITY) GetCell(SecurityCell->Flink); if( Before != After ) { // make sure the list will not remain empty Before->Flink = SecurityCell->Flink; After->Blink = SecurityCell->Blink; } FreeCell(NextCell); NextCell = SecurityCell->Flink; fprintf(stderr, " ... deleted"); } else { bRez = FALSE; if(CompactHive) { // any attempt to compact a corrupted hive will fail CompactHive = FALSE; fprintf(stderr, "\nRun chkreg /R to fix."); } } fprintf(stderr, "\n"); } else { // validate the next one LastCell = NextCell; NextCell = SecurityCell->Flink; } } while ( NextCell != ListAnchor ); return bRez; } BOOLEAN ChkSecurityCellInList(HCELL_INDEX Security) /*++ Routine Description: Searches the specified cell within the security descriptors list Arguments: Security - Provides the current cell Return Value: TRUE - the current cell was found in the security list FALSE - the current cell is not present in the security list and it couldn't be added. --*/ { PCM_KEY_NODE RootNode; PCM_KEY_SECURITY SecurityCell; PCM_KEY_SECURITY SecurityCellCurrent; PCM_KEY_SECURITY SecurityCellAfter; HCELL_INDEX ListAnchor; HCELL_INDEX NextCell; BOOLEAN bRez = TRUE; RootNode = (PCM_KEY_NODE) GetCell(RootCell); ListAnchor = NextCell = RootNode->Security; do { if( NextCell == Security) { // found it! return bRez; } SecurityCell = (PCM_KEY_SECURITY) GetCell(NextCell); NextCell = SecurityCell->Flink; } while ( NextCell != ListAnchor ); // cell not found; try to fix it bRez = FALSE; fprintf(stderr, "Security Cell (0x%lx) not in security descriptors list",Security); if(FixHive) { // // REPAIR: Add the security cell at the begining of the list // bRez = TRUE; SecurityCell = (PCM_KEY_SECURITY) GetCell(ListAnchor); SecurityCellCurrent = (PCM_KEY_SECURITY) GetCell(Security); SecurityCellAfter = (PCM_KEY_SECURITY) GetCell(SecurityCell->Flink); // restore the connections SecurityCellCurrent->Flink = SecurityCell->Flink; SecurityCellCurrent->Blink = ListAnchor; SecurityCell->Flink = Security; SecurityCellAfter->Blink = Security; fprintf(stderr, " ... security cell added to the list"); } else { if(CompactHive) { // any attempt to compact a corrupted hive will fail CompactHive = FALSE; fprintf(stderr, "\nRun chkreg /R to fix."); } } fprintf(stderr, "\n"); return bRez; }