/*++ Copyright (c) 1991 Microsoft Corporation Module Name: regfind.c Abstract: Utility to search all or part of the registry for a particular string value. The search string is a literal, the format of which depends upon the data type. REGFIND [-p KeyPath] [-n | -t DataType] searchString Will ennumerate and all the subkeys and values of KeyPath, applying itself recursively to each subkey it finds. For each value find that is of the appropriate type, it will search the value for a match. If found, it will print out the path and data of the value. The -n flag tells the program to search the names of keys and values for searchString and print out any that contain the searchString. Default KeyPath if none specified is \Registry Default DataType is any of the _SZ registry data types (REG_SZ, REG_EXPAND_SZ or REG_MULTI_SZ). Author: Steve Wood (stevewo) 08-Nov-95 Revision History: --*/ #include "regutil.h" void SearchValues( PWSTR KeyName, HKEY KeyHandle, ULONG Depth ); void SearchKeys( PWSTR KeyName, HKEY ParentKeyHandle, ULONG Depth ); BOOLEAN IgnoreCase; BOOLEAN SearchKeyAndValueNames; BOOLEAN IncludeBinaryDataInTextSearch; BOOLEAN LookForAnsiInBinaryData; BOOLEAN SearchingForMatchOnDataLength; ULONG SearchValueType; BOOLEAN ReplaceSZwithEXPAND_SZ; BOOLEAN SearchForMissingNULLs; BOOLEAN FixupMissingNULLs; #define REG_ANY_SZ 12 typedef struct _VALUE_BUFFER { PVOID Base; ULONG Length; ULONG MaximumLength; PWSTR CurrentDest; } VALUE_BUFFER, *PVALUE_BUFFER; PVALUE_BUFFER SearchBuffer; PVOID SearchData; ULONG SearchDataLength; WCHAR SearchData1UpperCase[ 2 ]; LPSTR SearchDataAnsi; ULONG SearchDataAnsiLength; UCHAR SearchDataAnsi1UpperCase[ 2 ]; PVALUE_BUFFER ReplacementBuffer; PVOID ReplacementData; ULONG ReplacementDataLength; LPSTR ReplacementDataAnsi; ULONG ReplacementDataAnsiLength; BOOLEAN AppendToValueBuffer( PVALUE_BUFFER p, PWSTR s ) { ULONG n, cb; n = wcslen( s ); cb = n * sizeof( WCHAR ); if ((cb + p->Length + sizeof( WCHAR )) >= p->MaximumLength) { return FALSE; } if (p->Length != 0) { *(p->CurrentDest)++ = L' '; p->Length += 1; } memcpy( p->CurrentDest, s, cb ); p->Length += cb; p->CurrentDest += n; return TRUE; } PVALUE_BUFFER AllocateValueBuffer( ULONG MaximumLength, PWSTR InitialContents ) { PVALUE_BUFFER p; p = (PVALUE_BUFFER)VirtualAlloc( NULL, MaximumLength, MEM_COMMIT, PAGE_READWRITE ); if (p != NULL) { p->Base = (p+1); p->MaximumLength = MaximumLength; p->Length = 0; p->CurrentDest = (PWSTR)p->Base; if (InitialContents != NULL) { AppendToValueBuffer( p, InitialContents ); } } return p; } PVOID ApplyReplacementBuffer( IN OUT PULONG ValueDataLength, IN PVOID Match, OUT PBOOLEAN BufferOverflow ) { ULONG LengthBeforeMatch; LONG DeltaLength; if (ReplacementBuffer == NULL) { return NULL; } LengthBeforeMatch = (ULONG)((PCHAR)Match - (PCHAR)OldValueBuffer); DeltaLength = ReplacementDataLength - SearchDataLength; if (DeltaLength <= 0) { memcpy( Match, ReplacementData, ReplacementDataLength ); Match = (PCHAR)Match + ReplacementDataLength; if (DeltaLength < 0) { memcpy( Match, (PCHAR)Match - DeltaLength, *ValueDataLength - LengthBeforeMatch - ReplacementDataLength ); } } else { if (*ValueDataLength + DeltaLength > OldValueBufferSize) { *BufferOverflow = TRUE; return NULL; } memmove( (PCHAR)Match + DeltaLength, Match, *ValueDataLength - LengthBeforeMatch ); memcpy( Match, ReplacementData, ReplacementDataLength ); Match = (PCHAR)Match + ReplacementDataLength; } *ValueDataLength += DeltaLength; return Match; } typedef struct _KEY_INFO { PWSTR Name; BOOLEAN NameDisplayed; } KEY_INFO, *PKEY_INFO; #define MAX_LEVELS 256 KEY_INFO KeyPathInfo[ MAX_LEVELS ]; void DisplayPath( PKEY_INFO p, ULONG Depth ) { ULONG i; for (i=0; i %d bytes)", ReplacementBuffer->MaximumLength, 0 ); } } else if (!AppendToValueBuffer( SearchBuffer, GetArgAsUnicode( s ) )) { FatalError( "searchString too long (> %d bytes)", SearchBuffer->MaximumLength, 0 ); } } } if (SearchKeyAndValueNames) { if (SearchValueType != REG_ANY_SZ) { Usage( "May not specify -n with -t", 0 ); } if (ReplacementBuffer != NULL) { Usage( "May not specify -n with -r", 0 ); } if (SearchForMissingNULLs) { Usage( "May not specify -n with -z", 0 ); } } if ((IncludeBinaryDataInTextSearch || IgnoreCase) && SearchValueType != REG_ANY_SZ && SearchValueType != REG_SZ && SearchValueType != REG_MULTI_SZ && SearchValueType != REG_EXPAND_SZ ) { Usage( "May not specify -b or -y with -t other than _SZ types", 0 ); } if (SearchForMissingNULLs) { if (SearchBuffer != NULL) { Usage( "May not specify -z with a searchString", 0 ); } if (SearchValueType != REG_ANY_SZ) { Usage( "May not specify -z with -t", 0 ); } if (IncludeBinaryDataInTextSearch) { Usage( "May not specify -z with -b", 0 ); } if (IgnoreCase) { Usage( "May not specify -z with -y", 0 ); } } else if (SearchBuffer == NULL && SearchValueType == REG_ANY_SZ) { Usage( "No search type or string specified", 0 ); } if (SearchBuffer != NULL && SearchValueType == REG_NONE) { Usage( "May not specify a searchString when searching for REG_NONE type", 0 ); } Error = RTConnectToRegistry( MachineName, HiveFileName, HiveRootName, Win95Path, Win95UserPath, &KeyName, &RegistryContext ); if (Error != NO_ERROR) { FatalError( "Unable to access registry specifed (%u)", Error, 0 ); } if (SearchBuffer != NULL) { RtlZeroMemory( &ParsedLine, sizeof( ParsedLine ) ); ParsedLine.ValueString = SearchBuffer->Base; if (!RTParseValueData( NULL, &ParsedLine, SearchBuffer->Base, SearchBuffer->MaximumLength, &Type, &SearchData, &SearchDataLength ) ) { if (Type == REG_BINARY && SearchDataLength != 0 && GetLastError() == ERROR_NO_DATA ) { if (SearchValueType != REG_BINARY) { FatalError( "May only search for REG_BINARY datalength with -t REG_BINARY\n", 0, 0 ); } if (ReplacementBuffer != NULL) { FatalError( "May not specify replacementString if searching for REG_BINARY datalength\n", 0, 0 ); } SearchingForMatchOnDataLength = TRUE; } else { FatalError( "Invalid searchString format (%u)", GetLastError(), 0 ); } } if (Type == REG_SZ || Type == REG_EXPAND_SZ) { SearchDataLength -= sizeof( UNICODE_NULL ); } if (SearchDataLength == 0) { FatalError( "Zero length search string specified", 0, 0 ); } if (IgnoreCase) { SearchData1UpperCase[ 0 ] = *(PWSTR)SearchData; SearchData1UpperCase[ 1 ] = UNICODE_NULL; _wcsupr( SearchData1UpperCase ); } if (LookForAnsiInBinaryData) { SearchDataAnsiLength = SearchDataLength / sizeof( WCHAR ); SearchDataAnsi = HeapAlloc( GetProcessHeap(), 0, SearchDataAnsiLength ); if (SearchDataAnsi == NULL) { FatalError( "Unable to allocate buffer for ANSI search string", 0, 0 ); } if (WideCharToMultiByte( CP_ACP, 0, SearchData, SearchDataAnsiLength, SearchDataAnsi, SearchDataAnsiLength, NULL, NULL ) != (LONG)SearchDataAnsiLength ) { FatalError( "Unable to get ANSI representation of search string", 0, 0 ); } if (IgnoreCase) { SearchDataAnsi1UpperCase[ 0 ] = *SearchDataAnsi; SearchDataAnsi1UpperCase[ 1 ] = '\0'; _strupr( SearchDataAnsi1UpperCase ); } } } if (ReplacementBuffer != NULL) { RtlZeroMemory( &ParsedLine, sizeof( ParsedLine ) ); ParsedLine.ValueString = ReplacementBuffer->Base; if (!RTParseValueData( NULL, &ParsedLine, ReplacementBuffer->Base, ReplacementBuffer->MaximumLength, &Type, &ReplacementData, &ReplacementDataLength ) ) { FatalError( "Invalid replacementString format (%u)", GetLastError(), 0 ); } if (Type == REG_SZ || Type == REG_EXPAND_SZ) { ReplacementDataLength -= sizeof( UNICODE_NULL ); } if (Type != SearchValueType && (SearchValueType == REG_ANY_SZ && Type != REG_SZ) || (SearchValueType == REG_SZ && Type != REG_EXPAND_SZ) ) { FatalError( "Incompatible search and replacement types", 0, 0 ); } if (Type == REG_EXPAND_SZ && SearchValueType == REG_SZ) { ReplaceSZwithEXPAND_SZ = TRUE; } if (LookForAnsiInBinaryData) { ReplacementDataAnsiLength = ReplacementDataLength / sizeof( WCHAR ); ReplacementDataAnsi = HeapAlloc( GetProcessHeap(), 0, ReplacementDataAnsiLength ); if (ReplacementDataAnsi == NULL) { FatalError( "Unable to allocate buffer for ANSI replacement string", 0, 0 ); } if (WideCharToMultiByte( CP_ACP, 0, ReplacementData, ReplacementDataAnsiLength, ReplacementDataAnsi, ReplacementDataAnsiLength, NULL, NULL ) != (LONG)ReplacementDataAnsiLength ) { FatalError( "Unable to get ANSI representation of replacement string", 0, 0 ); } } } // // Print name of the tree we are about to search // fprintf( stderr, "Scanning %ws registry tree\n", KeyName ); if (SearchForMissingNULLs) { fprintf( stderr, "Searching for any REG_SZ or REG_EXPAND_SZ value missing a trailing\n" " NULL character and/or whose length is not a multiple of the\n" " size of a Unicode character.\n" ); if (FixupMissingNULLs) { fprintf( stderr, "Will add the missing NULL whereever needed and/or adjust\n" " the length up to an even multiple of the size of a Unicode\n" " character.\n" ); } } else if (SearchingForMatchOnDataLength) { fprintf( stderr, "Searching for any REG_BINARY value with a length of %08x\n", SearchDataLength ); } else { if (SearchBuffer == NULL) { fprintf( stderr, "Searching for any match based on type\n" ); } else { fprintf( stderr, "%sSearch for '%ws'\n", IgnoreCase ? "Case Insensitive " : "", SearchBuffer->Base ); } fprintf( stderr, "Will match values of type:" ); if (SearchValueType == REG_ANY_SZ || SearchValueType == REG_SZ ) { fprintf( stderr, " REG_SZ" ); } if (SearchValueType == REG_ANY_SZ || SearchValueType == REG_EXPAND_SZ ) { fprintf( stderr, " REG_EXPAND_SZ" ); } if (SearchValueType == REG_ANY_SZ || SearchValueType == REG_MULTI_SZ ) { fprintf( stderr, " REG_MULTI_SZ" ); } if (SearchValueType == REG_DWORD) { fprintf( stderr, " REG_DWORD" ); } if (SearchValueType == REG_BINARY) { fprintf( stderr, " REG_BINARY" ); } if (SearchValueType == REG_QWORD) { fprintf( stderr, " REG_QWORD" ); } if (SearchValueType == REG_NONE) { fprintf( stderr, " REG_NONE" ); } fprintf( stderr, "\n" ); if (SearchKeyAndValueNames) { fprintf( stderr, "Search will include key or value names\n" ); } if (ReplacementBuffer != NULL) { fprintf( stderr, "Will replace each occurence with: '%ws'\n", ReplacementBuffer->Base ); if (ReplaceSZwithEXPAND_SZ) { fprintf( stderr, "Also each matching REG_SZ will have its type changed to REG_EXPAND_SZ\n" ); } } } SearchKeys( KeyName, RegistryContext.HiveRootHandle, 0 ); RTDisconnectFromRegistry( &RegistryContext ); return 0; } void SearchKeys( PWSTR KeyName, HKEY ParentKeyHandle, ULONG Depth ) { LONG Error; HKEY KeyHandle; ULONG SubKeyIndex; WCHAR SubKeyName[ MAX_PATH ]; ULONG SubKeyNameLength; FILETIME LastWriteTime; Error = RTOpenKey( &RegistryContext, ParentKeyHandle, KeyName, MAXIMUM_ALLOWED, REG_OPTION_OPEN_LINK, &KeyHandle ); if (Error != NO_ERROR) { if (Depth == 0) { FatalError( "Unable to open key '%ws' (%u)\n", (ULONG_PTR)KeyName, (ULONG)Error ); } return; } KeyPathInfo[ Depth ].Name = KeyName; KeyPathInfo[ Depth ].NameDisplayed = FALSE; // // Search node's values first. // SearchValues( KeyName, KeyHandle, Depth+1 ); // // Enumerate node's children and apply ourselves to each one // for (SubKeyIndex = 0; TRUE; SubKeyIndex++) { SubKeyNameLength = sizeof( SubKeyName ) / sizeof(WCHAR); Error = RTEnumerateKey( &RegistryContext, KeyHandle, SubKeyIndex, &LastWriteTime, &SubKeyNameLength, SubKeyName ); if (Error != NO_ERROR) { if (Error != ERROR_NO_MORE_ITEMS && Error != ERROR_ACCESS_DENIED) { fprintf( stderr, "RTEnumerateKey( %ws ) failed (%u), skipping\n", KeyName, Error ); } break; } SearchKeys( SubKeyName, KeyHandle, Depth+1 ); } RTCloseKey( &RegistryContext, KeyHandle ); return; } void SearchValues( PWSTR KeyName, HKEY KeyHandle, ULONG Depth ) { LONG Error; DWORD ValueIndex; DWORD ValueType; DWORD ValueNameLength; WCHAR ValueName[ MAX_PATH ]; DWORD ValueDataLength; PWSTR sBegin, sEnd, s, sMatch, sMatchUpper; ULONG i; BOOLEAN AttemptMatch, MatchFound, MatchedOnType, ReplacementMade, BufferOverflow; if (SearchKeyAndValueNames && wcsstr( KeyName, SearchData )) { DisplayPath( KeyPathInfo, Depth ); } for (ValueIndex = 0; TRUE; ValueIndex++) { ValueType = REG_NONE; ValueNameLength = sizeof( ValueName ) / sizeof( WCHAR ); ValueDataLength = OldValueBufferSize; Error = RTEnumerateValueKey( &RegistryContext, KeyHandle, ValueIndex, &ValueType, &ValueNameLength, ValueName, &ValueDataLength, OldValueBuffer ); if (Error == NO_ERROR) { try { MatchFound = FALSE; ReplacementMade = FALSE; BufferOverflow = FALSE; if (SearchForMissingNULLs) { if (ValueType == REG_SZ || ValueType == REG_EXPAND_SZ) { if (ValueDataLength & (sizeof(WCHAR)-1)) { MatchFound = TRUE; } else if (ValueDataLength == 0 || *(PWSTR)((PCHAR)OldValueBuffer + ValueDataLength - sizeof( WCHAR )) != UNICODE_NULL ) { MatchFound = TRUE; } if (MatchFound && FixupMissingNULLs) { ValueDataLength = (ValueDataLength+sizeof(WCHAR)-1) & ~(sizeof(WCHAR)-1); *(PWSTR)((PCHAR)OldValueBuffer + ValueDataLength) = UNICODE_NULL; ValueDataLength += sizeof( UNICODE_NULL ); Error = RTSetValueKey( &RegistryContext, KeyHandle, ValueName, ValueType, ValueDataLength, OldValueBuffer ); if (Error != NO_ERROR) { fprintf( stderr, "REGFIND: Error setting replacement value (%u)\n", Error ); } } } } else if (SearchKeyAndValueNames) { MatchFound = (BOOLEAN)(wcsstr( ValueName, SearchData ) != NULL ); } else { if (SearchValueType == REG_ANY_SZ && (ValueType == REG_SZ || ValueType == REG_EXPAND_SZ || ValueType == REG_MULTI_SZ || (ValueType == REG_BINARY && IncludeBinaryDataInTextSearch) ) || SearchValueType == ValueType || (ValueType == REG_BINARY && IncludeBinaryDataInTextSearch) && (SearchValueType == REG_SZ || SearchValueType == REG_EXPAND_SZ || SearchValueType == REG_MULTI_SZ ) ) { MatchedOnType = TRUE; } else { MatchedOnType = FALSE; } if (SearchBuffer == NULL) { MatchFound = MatchedOnType; } else { if (MatchedOnType && ValueDataLength != 0) { if (ValueType == REG_SZ || ValueType == REG_EXPAND_SZ || ValueType == REG_MULTI_SZ ) { if (SearchDataLength <= ValueDataLength) { sBegin = OldValueBuffer; sEnd = (PWSTR)((PCHAR)sBegin + ValueDataLength); s = sBegin; while ((PCHAR)s + SearchDataLength < (PCHAR)sEnd) { sMatch = wcschr( s, *(PWSTR)SearchData ); if (IgnoreCase) { sMatchUpper = wcschr( s, SearchData1UpperCase[ 0 ] ); if (sMatch == NULL || (sMatchUpper != NULL && sMatchUpper < sMatch) ) { sMatch = sMatchUpper; } } if (sMatch != NULL) { if ((!IgnoreCase && !wcsncmp( sMatch, (PWSTR)SearchData, SearchDataLength / sizeof( WCHAR ) ) ) || (IgnoreCase && !_wcsnicmp( sMatch, (PWSTR)SearchData, SearchDataLength / sizeof( WCHAR ) ) ) ) { MatchFound = TRUE; s = ApplyReplacementBuffer( &ValueDataLength, sMatch, &BufferOverflow ); if (s == NULL) { if (BufferOverflow) { fprintf( stderr, "REGFIND: Buffer overflow doing replacement", Error ); } break; } ReplacementMade = TRUE; sEnd = (PWSTR)((PCHAR)sBegin + ValueDataLength); } else { s = sMatch + 1; if (*s == UNICODE_NULL && ValueType != REG_MULTI_SZ ) { s += 1; } } } else { if (ValueType != REG_MULTI_SZ) { break; } while (*s++) { } } } } } else if (ValueType == REG_DWORD) { if (*(PULONG)SearchData == *(PULONG)OldValueBuffer) { MatchFound = TRUE; } } else if (ValueType == REG_QWORD) { if (*(PDWORDLONG)SearchData == *(PDWORDLONG)OldValueBuffer) { MatchFound = TRUE; } } else if (ValueType == REG_BINARY) { if (SearchingForMatchOnDataLength) { if (ValueDataLength == SearchDataLength) { MatchFound = TRUE; } } else { sBegin = OldValueBuffer; sEnd = (PWSTR)((PCHAR)sBegin + ValueDataLength); s = sBegin; while (((PCHAR)s + SearchDataLength) < (PCHAR)sEnd) { s = memchr( s, *(PBYTE)SearchData, (UINT)((PCHAR)sEnd - (PCHAR)s) ); if (s != NULL) { if ((ULONG)((PCHAR)sEnd - (PCHAR)s) >= SearchDataLength && !memcmp( s, SearchData, SearchDataLength ) ) { MatchFound = TRUE; s = ApplyReplacementBuffer( &ValueDataLength, s, &BufferOverflow ); if (s == NULL) { if (BufferOverflow) { fprintf( stderr, "REGFIND: Buffer overflow doing replacement", Error ); } break; } ReplacementMade = TRUE; sEnd = (PWSTR)((PCHAR)sBegin + ValueDataLength); } s = (PWSTR)((PCHAR)s + 1); } else { break; } } } } if (ReplacementMade) { if (ReplaceSZwithEXPAND_SZ && ValueType == REG_SZ) { ValueType = REG_EXPAND_SZ; } Error = RTSetValueKey( &RegistryContext, KeyHandle, ValueName, ValueType, ValueDataLength, OldValueBuffer ); if (Error != NO_ERROR) { fprintf( stderr, "REGFIND: Error setting replacement value (%u)\n", Error ); } } } } } if (MatchFound) { DisplayPath( KeyPathInfo, Depth ); RTFormatKeyValue( OutputWidth, (PREG_OUTPUT_ROUTINE)fprintf, stdout, FALSE, Depth * IndentMultiple, ValueName, ValueDataLength, ValueType, OldValueBuffer ); } } except( EXCEPTION_EXECUTE_HANDLER ) { fprintf( stderr, "REGFIND: Access violation searching value\n" ); } } else if (Error == ERROR_NO_MORE_ITEMS) { return; } else { if (DebugOutput) { fprintf( stderr, "REGFIND: RTEnumerateValueKey( %ws ) failed (%u)\n", KeyName, Error ); } return; } } }