1045 lines
39 KiB
C
1045 lines
39 KiB
C
/*++
|
||
|
||
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<Depth; i++) {
|
||
if (!p[ i ].NameDisplayed) {
|
||
p[ i ].NameDisplayed = TRUE;
|
||
RTFormatKeyName( (PREG_OUTPUT_ROUTINE)fprintf, stdout, i * IndentMultiple, p[ i ].Name );
|
||
printf( "\n" );
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
BOOL
|
||
CtrlCHandler(
|
||
IN ULONG CtrlType
|
||
)
|
||
{
|
||
RTDisconnectFromRegistry( &RegistryContext );
|
||
return FALSE;
|
||
}
|
||
|
||
int
|
||
__cdecl main(
|
||
int argc,
|
||
char *argv[]
|
||
)
|
||
{
|
||
char *s, *s1;
|
||
LONG Error;
|
||
ULONG Type;
|
||
PWSTR KeyName;
|
||
PWSTR SearchValueTypeString;
|
||
REG_UNICODE_PARSE ParsedLine;
|
||
|
||
|
||
InitCommonCode( CtrlCHandler,
|
||
"REGFIND",
|
||
"[-p RegistryKeyPath] [-z | -t DataType] [-b | -B] [-y] [-n]\n"
|
||
" [searchString [-r ReplacementString]]\n",
|
||
"-p RegistryKeyPath specifies where to start searching\n"
|
||
"\n"
|
||
"-t specifies which registry types to look at:\n"
|
||
" REG_SZ, REG_MULTI_SZ, REG_EXPAND_SZ\n"
|
||
" REG_DWORD, REG_BINARY, REG_NONE\n"
|
||
" Default is any of the _SZ types\n"
|
||
"\n"
|
||
"-b only valid with _SZ searches, and specifies that REGFIND should\n"
|
||
" look for occurrences of the searchString inside of REG_BINARY data.\n"
|
||
" May not be specified with a replacementString that is not the same length\n"
|
||
" as the searchString\n"
|
||
"\n"
|
||
"-B same as -b but also looks for ANSI version of string within REG_BINARY values.\n"
|
||
"\n"
|
||
"-y only valid with _SZ searches, and specifies that REGFIND should\n"
|
||
" ignore case when searching.\n"
|
||
"\n"
|
||
"-n specifies to include key and value names in the search.\n"
|
||
" May not specify -n with -t\n"
|
||
"\n"
|
||
"-z specifies to search for REG_SZ and REG_EXPAND_SZ values that\n"
|
||
" are missing a trailing null character and/or have a length that is\n"
|
||
" not a multiple of the size of a Unicode character. If -r is also\n"
|
||
" specified then any replacement string is ignored, and REGFIND will\n"
|
||
" add the missing null character and/or adjust the length up to an\n"
|
||
" even multiple of the size of a Unicode character.\n"
|
||
"\n"
|
||
"searchString is the value to search for. Use quotes if it contains\n"
|
||
" any spaces. If searchString is not specified, just searches based on type.\n"
|
||
"\n"
|
||
"-r replacementString is an optional replacement string to replace any\n"
|
||
" matches with.\n"
|
||
"\n"
|
||
"searchString and replacementString must be of the same type as specified\n"
|
||
"to the -t switch. For any of the _SZ types, it is just a string\n"
|
||
"For REG_DWORD, it is a single number (i.e. 0x1000 or 4096)\n"
|
||
"For REG_BINARY, it is a number specifing #bytes, optionally followed by \n"
|
||
"the actual bytes, with a separate number for each DWORD\n"
|
||
" (e.g. 0x06 0x12345678 0x1234)\n"
|
||
"If just the byte count is specified, then REGFIND will search for all\n"
|
||
"REG_BINARY values that have that length. May not search for length\n"
|
||
"and specify -r\n"
|
||
"\n"
|
||
"When doing replacements, REGFIND displays the value AFTER the replacement\n"
|
||
"has been. It is usually best to run REGFIND once without the -r switch\n"
|
||
"to see what will be change before it is changed.\n"
|
||
"\n"
|
||
);
|
||
|
||
IgnoreCase = FALSE;
|
||
SearchKeyAndValueNames = FALSE;
|
||
IncludeBinaryDataInTextSearch = FALSE;
|
||
LookForAnsiInBinaryData = FALSE;
|
||
SearchingForMatchOnDataLength = FALSE;
|
||
SearchValueType = REG_ANY_SZ;
|
||
SearchValueTypeString = NULL;
|
||
SearchBuffer = NULL;
|
||
ReplacementBuffer = NULL;
|
||
ReplaceSZwithEXPAND_SZ = FALSE;
|
||
|
||
KeyName = NULL;
|
||
while (--argc) {
|
||
s = *++argv;
|
||
if (*s == '-' || *s == '/') {
|
||
while (*++s) {
|
||
switch( tolower( *s ) ) {
|
||
case 'y':
|
||
IgnoreCase = TRUE;
|
||
break;
|
||
|
||
case 'n':
|
||
SearchKeyAndValueNames = TRUE;
|
||
break;
|
||
|
||
case 'z':
|
||
SearchForMissingNULLs = TRUE;
|
||
break;
|
||
|
||
case 'p':
|
||
if (!--argc) {
|
||
Usage( "Missing argument to -p switch", 0 );
|
||
}
|
||
|
||
KeyName = GetArgAsUnicode( *++argv );
|
||
break;
|
||
|
||
case 'b':
|
||
IncludeBinaryDataInTextSearch = TRUE;
|
||
if (*s == 'B') {
|
||
LookForAnsiInBinaryData = TRUE;
|
||
}
|
||
break;
|
||
|
||
case 't':
|
||
if (!--argc) {
|
||
Usage( "Missing argument to -t switch", 0 );
|
||
}
|
||
|
||
s1 = *++argv;
|
||
if (!_stricmp( s1, "REG_SZ" )) {
|
||
SearchValueType = REG_SZ;
|
||
}
|
||
else
|
||
if (!_stricmp( s1, "REG_EXPAND_SZ" )) {
|
||
SearchValueType = REG_EXPAND_SZ;
|
||
}
|
||
else
|
||
if (!_stricmp( s1, "REG_MULTI_SZ" )) {
|
||
SearchValueType = REG_MULTI_SZ;
|
||
}
|
||
else
|
||
if (!_stricmp( s1, "REG_DWORD" )) {
|
||
SearchValueType = REG_DWORD;
|
||
SearchValueTypeString = L"REG_DWORD";
|
||
}
|
||
else
|
||
if (!_stricmp( s1, "REG_BINARY" )) {
|
||
SearchValueType = REG_BINARY;
|
||
SearchValueTypeString = L"REG_BINARY";
|
||
}
|
||
else
|
||
if (!_stricmp( s1, "REG_NONE" )) {
|
||
SearchValueType = REG_NONE;
|
||
}
|
||
else {
|
||
Usage( "Invalid argument (%s) to the -t switch\n",
|
||
(ULONG_PTR)s1 );
|
||
}
|
||
break;
|
||
|
||
case 'r':
|
||
if (SearchForMissingNULLs) {
|
||
FixupMissingNULLs = TRUE;
|
||
}
|
||
else {
|
||
if (SearchBuffer == NULL) {
|
||
Usage( "May not specify -r without a searchString first", 0 );
|
||
}
|
||
|
||
ReplacementBuffer = AllocateValueBuffer( 63 * 1024,
|
||
SearchValueTypeString
|
||
);
|
||
if (ReplacementBuffer == NULL) {
|
||
FatalError( "Unable to allocate buffer for replacement string", 0, 0 );
|
||
}
|
||
}
|
||
break;
|
||
|
||
default:
|
||
CommonSwitchProcessing( &argc, &argv, *s );
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
if (SearchBuffer == NULL) {
|
||
SearchBuffer = AllocateValueBuffer( 63 * 1024,
|
||
SearchValueTypeString
|
||
);
|
||
if (SearchBuffer == NULL) {
|
||
FatalError( "Unable to allocate buffer for search string", 0, 0 );
|
||
}
|
||
}
|
||
|
||
if (ReplacementBuffer != NULL) {
|
||
if (!AppendToValueBuffer( ReplacementBuffer, GetArgAsUnicode( s ) )) {
|
||
FatalError( "replacementString too long (> %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_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_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;
|
||
}
|
||
}
|
||
}
|