Leaked source code of windows server 2003
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.

1058 lines
40 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. regfind.c
  5. Abstract:
  6. Utility to search all or part of the registry for a particular
  7. string value. The search string is a literal, the format of which
  8. depends upon the data type.
  9. REGFIND [-p KeyPath] [-n | -t DataType] searchString
  10. Will ennumerate and all the subkeys and values of KeyPath,
  11. applying itself recursively to each subkey it finds. For each
  12. value find that is of the appropriate type, it will search the
  13. value for a match. If found, it will print out the path and data
  14. of the value. The -n flag tells the program to search the names
  15. of keys and values for searchString and print out any that contain
  16. the searchString.
  17. Default KeyPath if none specified is \Registry
  18. Default DataType is any of the _SZ registry data types (REG_SZ,
  19. REG_EXPAND_SZ or REG_MULTI_SZ).
  20. Author:
  21. Steve Wood (stevewo) 08-Nov-95
  22. Revision History:
  23. --*/
  24. #include "regutil.h"
  25. void
  26. SearchValues(
  27. PWSTR KeyName,
  28. HKEY KeyHandle,
  29. ULONG Depth
  30. );
  31. void
  32. SearchKeys(
  33. PWSTR KeyName,
  34. HKEY ParentKeyHandle,
  35. ULONG Depth
  36. );
  37. BOOLEAN IgnoreCase;
  38. BOOLEAN SearchKeyAndValueNames;
  39. BOOLEAN IncludeBinaryDataInTextSearch;
  40. BOOLEAN LookForAnsiInBinaryData;
  41. BOOLEAN SearchingForMatchOnDataLength;
  42. ULONG SearchValueType;
  43. BOOLEAN ReplaceSZwithEXPAND_SZ;
  44. BOOLEAN SearchForMissingNULLs;
  45. BOOLEAN FixupMissingNULLs;
  46. #define REG_ANY_SZ 12
  47. typedef struct _VALUE_BUFFER {
  48. PVOID Base;
  49. ULONG Length;
  50. ULONG MaximumLength;
  51. PWSTR CurrentDest;
  52. } VALUE_BUFFER, *PVALUE_BUFFER;
  53. PVALUE_BUFFER SearchBuffer;
  54. PVOID SearchData;
  55. ULONG SearchDataLength;
  56. WCHAR SearchData1UpperCase[ 2 ];
  57. LPSTR SearchDataAnsi;
  58. ULONG SearchDataAnsiLength;
  59. UCHAR SearchDataAnsi1UpperCase[ 2 ];
  60. PVALUE_BUFFER ReplacementBuffer;
  61. PVOID ReplacementData;
  62. ULONG ReplacementDataLength;
  63. LPSTR ReplacementDataAnsi;
  64. ULONG ReplacementDataAnsiLength;
  65. BOOLEAN
  66. AppendToValueBuffer(
  67. PVALUE_BUFFER p,
  68. PWSTR s
  69. )
  70. {
  71. ULONG n, cb;
  72. n = wcslen( s );
  73. cb = n * sizeof( WCHAR );
  74. if ((cb + p->Length + sizeof( WCHAR )) >= p->MaximumLength) {
  75. return FALSE;
  76. }
  77. if (p->Length != 0) {
  78. *(p->CurrentDest)++ = L' ';
  79. p->Length += 1;
  80. }
  81. memcpy( p->CurrentDest, s, cb );
  82. p->Length += cb;
  83. p->CurrentDest += n;
  84. return TRUE;
  85. }
  86. PVALUE_BUFFER
  87. AllocateValueBuffer(
  88. ULONG MaximumLength,
  89. PWSTR InitialContents
  90. )
  91. {
  92. PVALUE_BUFFER p;
  93. p = (PVALUE_BUFFER)VirtualAlloc( NULL, MaximumLength, MEM_COMMIT, PAGE_READWRITE );
  94. if (p != NULL) {
  95. p->Base = (p+1);
  96. p->MaximumLength = MaximumLength;
  97. p->Length = 0;
  98. p->CurrentDest = (PWSTR)p->Base;
  99. if (InitialContents != NULL) {
  100. AppendToValueBuffer( p, InitialContents );
  101. }
  102. }
  103. return p;
  104. }
  105. PVOID
  106. ApplyReplacementBuffer(
  107. IN OUT PULONG ValueDataLength,
  108. IN PVOID Match,
  109. OUT PBOOLEAN BufferOverflow
  110. )
  111. {
  112. ULONG LengthBeforeMatch;
  113. LONG DeltaLength;
  114. if (ReplacementBuffer == NULL) {
  115. return NULL;
  116. }
  117. LengthBeforeMatch = (ULONG)((PCHAR)Match - (PCHAR)OldValueBuffer);
  118. DeltaLength = ReplacementDataLength - SearchDataLength;
  119. if (DeltaLength <= 0) {
  120. memcpy( Match, ReplacementData, ReplacementDataLength );
  121. Match = (PCHAR)Match + ReplacementDataLength;
  122. if (DeltaLength < 0) {
  123. memcpy( Match,
  124. (PCHAR)Match - DeltaLength,
  125. *ValueDataLength - LengthBeforeMatch - ReplacementDataLength
  126. );
  127. }
  128. }
  129. else {
  130. if (*ValueDataLength + DeltaLength > OldValueBufferSize) {
  131. *BufferOverflow = TRUE;
  132. return NULL;
  133. }
  134. memmove( (PCHAR)Match + DeltaLength,
  135. Match,
  136. *ValueDataLength - LengthBeforeMatch
  137. );
  138. memcpy( Match, ReplacementData, ReplacementDataLength );
  139. Match = (PCHAR)Match + ReplacementDataLength;
  140. }
  141. *ValueDataLength += DeltaLength;
  142. return Match;
  143. }
  144. typedef struct _KEY_INFO {
  145. PWSTR Name;
  146. BOOLEAN NameDisplayed;
  147. } KEY_INFO, *PKEY_INFO;
  148. #define MAX_LEVELS 256
  149. KEY_INFO KeyPathInfo[ MAX_LEVELS ];
  150. void
  151. DisplayPath(
  152. PKEY_INFO p,
  153. ULONG Depth
  154. )
  155. {
  156. ULONG i;
  157. for (i=0; i<Depth; i++) {
  158. if (!p[ i ].NameDisplayed) {
  159. p[ i ].NameDisplayed = TRUE;
  160. RTFormatKeyName( (PREG_OUTPUT_ROUTINE)fprintf, stdout, i * IndentMultiple, p[ i ].Name );
  161. printf( "\n" );
  162. }
  163. }
  164. }
  165. BOOL
  166. CtrlCHandler(
  167. IN ULONG CtrlType
  168. )
  169. {
  170. RTDisconnectFromRegistry( &RegistryContext );
  171. return FALSE;
  172. }
  173. int
  174. __cdecl main(
  175. int argc,
  176. char *argv[]
  177. )
  178. {
  179. char *s, *s1;
  180. LONG Error;
  181. ULONG Type;
  182. PWSTR KeyName;
  183. PWSTR SearchValueTypeString;
  184. REG_UNICODE_PARSE ParsedLine;
  185. InitCommonCode( CtrlCHandler,
  186. "REGFIND",
  187. "[-p RegistryKeyPath] [-z | -t DataType] [-b | -B] [-y] [-n]\n"
  188. " [searchString [-r ReplacementString]]\n",
  189. "-p RegistryKeyPath specifies where to start searching\n"
  190. "\n"
  191. "-t specifies which registry types to look at:\n"
  192. " REG_SZ, REG_MULTI_SZ, REG_EXPAND_SZ\n"
  193. " REG_DWORD, REG_BINARY, REG_QWORD, REG_NONE\n"
  194. " Default is any of the _SZ types\n"
  195. "\n"
  196. "-b only valid with _SZ searches, and specifies that REGFIND should\n"
  197. " look for occurrences of the searchString inside of REG_BINARY data.\n"
  198. " May not be specified with a replacementString that is not the same length\n"
  199. " as the searchString\n"
  200. "\n"
  201. "-B same as -b but also looks for ANSI version of string within REG_BINARY values.\n"
  202. "\n"
  203. "-y only valid with _SZ searches, and specifies that REGFIND should\n"
  204. " ignore case when searching.\n"
  205. "\n"
  206. "-n specifies to include key and value names in the search.\n"
  207. " May not specify -n with -t\n"
  208. "\n"
  209. "-z specifies to search for REG_SZ and REG_EXPAND_SZ values that\n"
  210. " are missing a trailing null character and/or have a length that is\n"
  211. " not a multiple of the size of a Unicode character. If -r is also\n"
  212. " specified then any replacement string is ignored, and REGFIND will\n"
  213. " add the missing null character and/or adjust the length up to an\n"
  214. " even multiple of the size of a Unicode character.\n"
  215. "\n"
  216. "searchString is the value to search for. Use quotes if it contains\n"
  217. " any spaces. If searchString is not specified, just searches based on type.\n"
  218. "\n"
  219. "-r replacementString is an optional replacement string to replace any\n"
  220. " matches with.\n"
  221. "\n"
  222. "searchString and replacementString must be of the same type as specified\n"
  223. "to the -t switch. For any of the _SZ types, it is just a string\n"
  224. "For REG_DWORD or REG_QWORD, it is a single number (i.e. 0x1000 or 4096)\n"
  225. "For REG_BINARY, it is a number specifing #bytes, optionally followed by \n"
  226. "the actual bytes, with a separate number for each DWORD\n"
  227. " (e.g. 0x06 0x12345678 0x1234)\n"
  228. "If just the byte count is specified, then REGFIND will search for all\n"
  229. "REG_BINARY values that have that length. May not search for length\n"
  230. "and specify -r\n"
  231. "\n"
  232. "When doing replacements, REGFIND displays the value AFTER the replacement\n"
  233. "has been. It is usually best to run REGFIND once without the -r switch\n"
  234. "to see what will be change before it is changed.\n"
  235. "\n"
  236. );
  237. IgnoreCase = FALSE;
  238. SearchKeyAndValueNames = FALSE;
  239. IncludeBinaryDataInTextSearch = FALSE;
  240. LookForAnsiInBinaryData = FALSE;
  241. SearchingForMatchOnDataLength = FALSE;
  242. SearchValueType = REG_ANY_SZ;
  243. SearchValueTypeString = NULL;
  244. SearchBuffer = NULL;
  245. ReplacementBuffer = NULL;
  246. ReplaceSZwithEXPAND_SZ = FALSE;
  247. KeyName = NULL;
  248. while (--argc) {
  249. s = *++argv;
  250. if (*s == '-' || *s == '/') {
  251. while (*++s) {
  252. switch( tolower( *s ) ) {
  253. case 'y':
  254. IgnoreCase = TRUE;
  255. break;
  256. case 'n':
  257. SearchKeyAndValueNames = TRUE;
  258. break;
  259. case 'z':
  260. SearchForMissingNULLs = TRUE;
  261. break;
  262. case 'p':
  263. if (!--argc) {
  264. Usage( "Missing argument to -p switch", 0 );
  265. }
  266. KeyName = GetArgAsUnicode( *++argv );
  267. break;
  268. case 'b':
  269. IncludeBinaryDataInTextSearch = TRUE;
  270. if (*s == 'B') {
  271. LookForAnsiInBinaryData = TRUE;
  272. }
  273. break;
  274. case 't':
  275. if (!--argc) {
  276. Usage( "Missing argument to -t switch", 0 );
  277. }
  278. s1 = *++argv;
  279. if (!_stricmp( s1, "REG_SZ" )) {
  280. SearchValueType = REG_SZ;
  281. }
  282. else
  283. if (!_stricmp( s1, "REG_EXPAND_SZ" )) {
  284. SearchValueType = REG_EXPAND_SZ;
  285. }
  286. else
  287. if (!_stricmp( s1, "REG_MULTI_SZ" )) {
  288. SearchValueType = REG_MULTI_SZ;
  289. }
  290. else
  291. if (!_stricmp( s1, "REG_DWORD" )) {
  292. SearchValueType = REG_DWORD;
  293. SearchValueTypeString = L"REG_DWORD";
  294. }
  295. else
  296. if (!_stricmp( s1, "REG_BINARY" )) {
  297. SearchValueType = REG_BINARY;
  298. SearchValueTypeString = L"REG_BINARY";
  299. }
  300. else
  301. if (!_stricmp( s1, "REG_QWORD" )) {
  302. SearchValueType = REG_QWORD;
  303. SearchValueTypeString = L"REG_QWORD";
  304. }
  305. else
  306. if (!_stricmp( s1, "REG_NONE" )) {
  307. SearchValueType = REG_NONE;
  308. }
  309. else {
  310. Usage( "Invalid argument (%s) to the -t switch\n",
  311. (ULONG_PTR)s1 );
  312. }
  313. break;
  314. case 'r':
  315. if (SearchForMissingNULLs) {
  316. FixupMissingNULLs = TRUE;
  317. }
  318. else {
  319. if (SearchBuffer == NULL) {
  320. Usage( "May not specify -r without a searchString first", 0 );
  321. }
  322. ReplacementBuffer = AllocateValueBuffer( 63 * 1024,
  323. SearchValueTypeString
  324. );
  325. if (ReplacementBuffer == NULL) {
  326. FatalError( "Unable to allocate buffer for replacement string", 0, 0 );
  327. }
  328. }
  329. break;
  330. default:
  331. CommonSwitchProcessing( &argc, &argv, *s );
  332. }
  333. }
  334. }
  335. else {
  336. if (SearchBuffer == NULL) {
  337. SearchBuffer = AllocateValueBuffer( 63 * 1024,
  338. SearchValueTypeString
  339. );
  340. if (SearchBuffer == NULL) {
  341. FatalError( "Unable to allocate buffer for search string", 0, 0 );
  342. }
  343. }
  344. if (ReplacementBuffer != NULL) {
  345. if (!AppendToValueBuffer( ReplacementBuffer, GetArgAsUnicode( s ) )) {
  346. FatalError( "replacementString too long (> %d bytes)", ReplacementBuffer->MaximumLength, 0 );
  347. }
  348. }
  349. else
  350. if (!AppendToValueBuffer( SearchBuffer, GetArgAsUnicode( s ) )) {
  351. FatalError( "searchString too long (> %d bytes)", SearchBuffer->MaximumLength, 0 );
  352. }
  353. }
  354. }
  355. if (SearchKeyAndValueNames) {
  356. if (SearchValueType != REG_ANY_SZ) {
  357. Usage( "May not specify -n with -t", 0 );
  358. }
  359. if (ReplacementBuffer != NULL) {
  360. Usage( "May not specify -n with -r", 0 );
  361. }
  362. if (SearchForMissingNULLs) {
  363. Usage( "May not specify -n with -z", 0 );
  364. }
  365. }
  366. if ((IncludeBinaryDataInTextSearch || IgnoreCase) &&
  367. SearchValueType != REG_ANY_SZ &&
  368. SearchValueType != REG_SZ &&
  369. SearchValueType != REG_MULTI_SZ &&
  370. SearchValueType != REG_EXPAND_SZ
  371. ) {
  372. Usage( "May not specify -b or -y with -t other than _SZ types", 0 );
  373. }
  374. if (SearchForMissingNULLs) {
  375. if (SearchBuffer != NULL) {
  376. Usage( "May not specify -z with a searchString", 0 );
  377. }
  378. if (SearchValueType != REG_ANY_SZ) {
  379. Usage( "May not specify -z with -t", 0 );
  380. }
  381. if (IncludeBinaryDataInTextSearch) {
  382. Usage( "May not specify -z with -b", 0 );
  383. }
  384. if (IgnoreCase) {
  385. Usage( "May not specify -z with -y", 0 );
  386. }
  387. }
  388. else
  389. if (SearchBuffer == NULL && SearchValueType == REG_ANY_SZ) {
  390. Usage( "No search type or string specified", 0 );
  391. }
  392. if (SearchBuffer != NULL && SearchValueType == REG_NONE) {
  393. Usage( "May not specify a searchString when searching for REG_NONE type", 0 );
  394. }
  395. Error = RTConnectToRegistry( MachineName,
  396. HiveFileName,
  397. HiveRootName,
  398. Win95Path,
  399. Win95UserPath,
  400. &KeyName,
  401. &RegistryContext
  402. );
  403. if (Error != NO_ERROR) {
  404. FatalError( "Unable to access registry specifed (%u)", Error, 0 );
  405. }
  406. if (SearchBuffer != NULL) {
  407. RtlZeroMemory( &ParsedLine, sizeof( ParsedLine ) );
  408. ParsedLine.ValueString = SearchBuffer->Base;
  409. if (!RTParseValueData( NULL,
  410. &ParsedLine,
  411. SearchBuffer->Base,
  412. SearchBuffer->MaximumLength,
  413. &Type,
  414. &SearchData,
  415. &SearchDataLength
  416. )
  417. ) {
  418. if (Type == REG_BINARY &&
  419. SearchDataLength != 0 &&
  420. GetLastError() == ERROR_NO_DATA
  421. ) {
  422. if (SearchValueType != REG_BINARY) {
  423. FatalError( "May only search for REG_BINARY datalength with -t REG_BINARY\n", 0, 0 );
  424. }
  425. if (ReplacementBuffer != NULL) {
  426. FatalError( "May not specify replacementString if searching for REG_BINARY datalength\n", 0, 0 );
  427. }
  428. SearchingForMatchOnDataLength = TRUE;
  429. }
  430. else {
  431. FatalError( "Invalid searchString format (%u)", GetLastError(), 0 );
  432. }
  433. }
  434. if (Type == REG_SZ || Type == REG_EXPAND_SZ) {
  435. SearchDataLength -= sizeof( UNICODE_NULL );
  436. }
  437. if (SearchDataLength == 0) {
  438. FatalError( "Zero length search string specified", 0, 0 );
  439. }
  440. if (IgnoreCase) {
  441. SearchData1UpperCase[ 0 ] = *(PWSTR)SearchData;
  442. SearchData1UpperCase[ 1 ] = UNICODE_NULL;
  443. _wcsupr( SearchData1UpperCase );
  444. }
  445. if (LookForAnsiInBinaryData) {
  446. SearchDataAnsiLength = SearchDataLength / sizeof( WCHAR );
  447. SearchDataAnsi = HeapAlloc( GetProcessHeap(), 0, SearchDataAnsiLength );
  448. if (SearchDataAnsi == NULL) {
  449. FatalError( "Unable to allocate buffer for ANSI search string", 0, 0 );
  450. }
  451. if (WideCharToMultiByte( CP_ACP,
  452. 0,
  453. SearchData,
  454. SearchDataAnsiLength,
  455. SearchDataAnsi,
  456. SearchDataAnsiLength,
  457. NULL,
  458. NULL
  459. ) != (LONG)SearchDataAnsiLength
  460. ) {
  461. FatalError( "Unable to get ANSI representation of search string", 0, 0 );
  462. }
  463. if (IgnoreCase) {
  464. SearchDataAnsi1UpperCase[ 0 ] = *SearchDataAnsi;
  465. SearchDataAnsi1UpperCase[ 1 ] = '\0';
  466. _strupr( SearchDataAnsi1UpperCase );
  467. }
  468. }
  469. }
  470. if (ReplacementBuffer != NULL) {
  471. RtlZeroMemory( &ParsedLine, sizeof( ParsedLine ) );
  472. ParsedLine.ValueString = ReplacementBuffer->Base;
  473. if (!RTParseValueData( NULL,
  474. &ParsedLine,
  475. ReplacementBuffer->Base,
  476. ReplacementBuffer->MaximumLength,
  477. &Type,
  478. &ReplacementData,
  479. &ReplacementDataLength
  480. )
  481. ) {
  482. FatalError( "Invalid replacementString format (%u)", GetLastError(), 0 );
  483. }
  484. if (Type == REG_SZ || Type == REG_EXPAND_SZ) {
  485. ReplacementDataLength -= sizeof( UNICODE_NULL );
  486. }
  487. if (Type != SearchValueType &&
  488. (SearchValueType == REG_ANY_SZ && Type != REG_SZ) ||
  489. (SearchValueType == REG_SZ && Type != REG_EXPAND_SZ)
  490. ) {
  491. FatalError( "Incompatible search and replacement types", 0, 0 );
  492. }
  493. if (Type == REG_EXPAND_SZ && SearchValueType == REG_SZ) {
  494. ReplaceSZwithEXPAND_SZ = TRUE;
  495. }
  496. if (LookForAnsiInBinaryData) {
  497. ReplacementDataAnsiLength = ReplacementDataLength / sizeof( WCHAR );
  498. ReplacementDataAnsi = HeapAlloc( GetProcessHeap(), 0, ReplacementDataAnsiLength );
  499. if (ReplacementDataAnsi == NULL) {
  500. FatalError( "Unable to allocate buffer for ANSI replacement string", 0, 0 );
  501. }
  502. if (WideCharToMultiByte( CP_ACP,
  503. 0,
  504. ReplacementData,
  505. ReplacementDataAnsiLength,
  506. ReplacementDataAnsi,
  507. ReplacementDataAnsiLength,
  508. NULL,
  509. NULL
  510. ) != (LONG)ReplacementDataAnsiLength
  511. ) {
  512. FatalError( "Unable to get ANSI representation of replacement string", 0, 0 );
  513. }
  514. }
  515. }
  516. //
  517. // Print name of the tree we are about to search
  518. //
  519. fprintf( stderr, "Scanning %ws registry tree\n", KeyName );
  520. if (SearchForMissingNULLs) {
  521. fprintf( stderr,
  522. "Searching for any REG_SZ or REG_EXPAND_SZ value missing a trailing\n"
  523. " NULL character and/or whose length is not a multiple of the\n"
  524. " size of a Unicode character.\n"
  525. );
  526. if (FixupMissingNULLs) {
  527. fprintf( stderr,
  528. "Will add the missing NULL whereever needed and/or adjust\n"
  529. " the length up to an even multiple of the size of a Unicode\n"
  530. " character.\n"
  531. );
  532. }
  533. }
  534. else
  535. if (SearchingForMatchOnDataLength) {
  536. fprintf( stderr,
  537. "Searching for any REG_BINARY value with a length of %08x\n",
  538. SearchDataLength
  539. );
  540. }
  541. else {
  542. if (SearchBuffer == NULL) {
  543. fprintf( stderr, "Searching for any match based on type\n" );
  544. }
  545. else {
  546. fprintf( stderr,
  547. "%sSearch for '%ws'\n",
  548. IgnoreCase ? "Case Insensitive " : "",
  549. SearchBuffer->Base
  550. );
  551. }
  552. fprintf( stderr, "Will match values of type:" );
  553. if (SearchValueType == REG_ANY_SZ ||
  554. SearchValueType == REG_SZ
  555. ) {
  556. fprintf( stderr, " REG_SZ" );
  557. }
  558. if (SearchValueType == REG_ANY_SZ ||
  559. SearchValueType == REG_EXPAND_SZ
  560. ) {
  561. fprintf( stderr, " REG_EXPAND_SZ" );
  562. }
  563. if (SearchValueType == REG_ANY_SZ ||
  564. SearchValueType == REG_MULTI_SZ
  565. ) {
  566. fprintf( stderr, " REG_MULTI_SZ" );
  567. }
  568. if (SearchValueType == REG_DWORD) {
  569. fprintf( stderr, " REG_DWORD" );
  570. }
  571. if (SearchValueType == REG_BINARY) {
  572. fprintf( stderr, " REG_BINARY" );
  573. }
  574. if (SearchValueType == REG_QWORD) {
  575. fprintf( stderr, " REG_QWORD" );
  576. }
  577. if (SearchValueType == REG_NONE) {
  578. fprintf( stderr, " REG_NONE" );
  579. }
  580. fprintf( stderr, "\n" );
  581. if (SearchKeyAndValueNames) {
  582. fprintf( stderr, "Search will include key or value names\n" );
  583. }
  584. if (ReplacementBuffer != NULL) {
  585. fprintf( stderr, "Will replace each occurence with: '%ws'\n",
  586. ReplacementBuffer->Base
  587. );
  588. if (ReplaceSZwithEXPAND_SZ) {
  589. fprintf( stderr, "Also each matching REG_SZ will have its type changed to REG_EXPAND_SZ\n" );
  590. }
  591. }
  592. }
  593. SearchKeys( KeyName, RegistryContext.HiveRootHandle, 0 );
  594. RTDisconnectFromRegistry( &RegistryContext );
  595. return 0;
  596. }
  597. void
  598. SearchKeys(
  599. PWSTR KeyName,
  600. HKEY ParentKeyHandle,
  601. ULONG Depth
  602. )
  603. {
  604. LONG Error;
  605. HKEY KeyHandle;
  606. ULONG SubKeyIndex;
  607. WCHAR SubKeyName[ MAX_PATH ];
  608. ULONG SubKeyNameLength;
  609. FILETIME LastWriteTime;
  610. Error = RTOpenKey( &RegistryContext,
  611. ParentKeyHandle,
  612. KeyName,
  613. MAXIMUM_ALLOWED,
  614. REG_OPTION_OPEN_LINK,
  615. &KeyHandle
  616. );
  617. if (Error != NO_ERROR) {
  618. if (Depth == 0) {
  619. FatalError( "Unable to open key '%ws' (%u)\n",
  620. (ULONG_PTR)KeyName,
  621. (ULONG)Error
  622. );
  623. }
  624. return;
  625. }
  626. KeyPathInfo[ Depth ].Name = KeyName;
  627. KeyPathInfo[ Depth ].NameDisplayed = FALSE;
  628. //
  629. // Search node's values first.
  630. //
  631. SearchValues( KeyName, KeyHandle, Depth+1 );
  632. //
  633. // Enumerate node's children and apply ourselves to each one
  634. //
  635. for (SubKeyIndex = 0; TRUE; SubKeyIndex++) {
  636. SubKeyNameLength = sizeof( SubKeyName ) / sizeof(WCHAR);
  637. Error = RTEnumerateKey( &RegistryContext,
  638. KeyHandle,
  639. SubKeyIndex,
  640. &LastWriteTime,
  641. &SubKeyNameLength,
  642. SubKeyName
  643. );
  644. if (Error != NO_ERROR) {
  645. if (Error != ERROR_NO_MORE_ITEMS && Error != ERROR_ACCESS_DENIED) {
  646. fprintf( stderr,
  647. "RTEnumerateKey( %ws ) failed (%u), skipping\n",
  648. KeyName,
  649. Error
  650. );
  651. }
  652. break;
  653. }
  654. SearchKeys( SubKeyName, KeyHandle, Depth+1 );
  655. }
  656. RTCloseKey( &RegistryContext, KeyHandle );
  657. return;
  658. }
  659. void
  660. SearchValues(
  661. PWSTR KeyName,
  662. HKEY KeyHandle,
  663. ULONG Depth
  664. )
  665. {
  666. LONG Error;
  667. DWORD ValueIndex;
  668. DWORD ValueType;
  669. DWORD ValueNameLength;
  670. WCHAR ValueName[ MAX_PATH ];
  671. DWORD ValueDataLength;
  672. PWSTR sBegin, sEnd, s, sMatch, sMatchUpper;
  673. ULONG i;
  674. BOOLEAN AttemptMatch, MatchFound, MatchedOnType, ReplacementMade, BufferOverflow;
  675. if (SearchKeyAndValueNames && wcsstr( KeyName, SearchData )) {
  676. DisplayPath( KeyPathInfo, Depth );
  677. }
  678. for (ValueIndex = 0; TRUE; ValueIndex++) {
  679. ValueType = REG_NONE;
  680. ValueNameLength = sizeof( ValueName ) / sizeof( WCHAR );
  681. ValueDataLength = OldValueBufferSize;
  682. Error = RTEnumerateValueKey( &RegistryContext,
  683. KeyHandle,
  684. ValueIndex,
  685. &ValueType,
  686. &ValueNameLength,
  687. ValueName,
  688. &ValueDataLength,
  689. OldValueBuffer
  690. );
  691. if (Error == NO_ERROR) {
  692. try {
  693. MatchFound = FALSE;
  694. ReplacementMade = FALSE;
  695. BufferOverflow = FALSE;
  696. if (SearchForMissingNULLs) {
  697. if (ValueType == REG_SZ || ValueType == REG_EXPAND_SZ) {
  698. if (ValueDataLength & (sizeof(WCHAR)-1)) {
  699. MatchFound = TRUE;
  700. }
  701. else
  702. if (ValueDataLength == 0 ||
  703. *(PWSTR)((PCHAR)OldValueBuffer + ValueDataLength - sizeof( WCHAR )) != UNICODE_NULL
  704. ) {
  705. MatchFound = TRUE;
  706. }
  707. if (MatchFound && FixupMissingNULLs) {
  708. ValueDataLength = (ValueDataLength+sizeof(WCHAR)-1) & ~(sizeof(WCHAR)-1);
  709. *(PWSTR)((PCHAR)OldValueBuffer + ValueDataLength) = UNICODE_NULL;
  710. ValueDataLength += sizeof( UNICODE_NULL );
  711. Error = RTSetValueKey( &RegistryContext,
  712. KeyHandle,
  713. ValueName,
  714. ValueType,
  715. ValueDataLength,
  716. OldValueBuffer
  717. );
  718. if (Error != NO_ERROR) {
  719. fprintf( stderr,
  720. "REGFIND: Error setting replacement value (%u)\n",
  721. Error
  722. );
  723. }
  724. }
  725. }
  726. }
  727. else
  728. if (SearchKeyAndValueNames) {
  729. MatchFound = (BOOLEAN)(wcsstr( ValueName,
  730. SearchData
  731. ) != NULL
  732. );
  733. }
  734. else {
  735. if (SearchValueType == REG_ANY_SZ &&
  736. (ValueType == REG_SZ ||
  737. ValueType == REG_EXPAND_SZ ||
  738. ValueType == REG_MULTI_SZ ||
  739. (ValueType == REG_BINARY && IncludeBinaryDataInTextSearch)
  740. ) ||
  741. SearchValueType == ValueType ||
  742. (ValueType == REG_BINARY && IncludeBinaryDataInTextSearch) &&
  743. (SearchValueType == REG_SZ ||
  744. SearchValueType == REG_EXPAND_SZ ||
  745. SearchValueType == REG_MULTI_SZ
  746. )
  747. ) {
  748. MatchedOnType = TRUE;
  749. }
  750. else {
  751. MatchedOnType = FALSE;
  752. }
  753. if (SearchBuffer == NULL) {
  754. MatchFound = MatchedOnType;
  755. }
  756. else {
  757. if (MatchedOnType && ValueDataLength != 0) {
  758. if (ValueType == REG_SZ ||
  759. ValueType == REG_EXPAND_SZ ||
  760. ValueType == REG_MULTI_SZ
  761. ) {
  762. if (SearchDataLength <= ValueDataLength) {
  763. sBegin = OldValueBuffer;
  764. sEnd = (PWSTR)((PCHAR)sBegin + ValueDataLength);
  765. s = sBegin;
  766. while ((PCHAR)s + SearchDataLength < (PCHAR)sEnd) {
  767. sMatch = wcschr( s, *(PWSTR)SearchData );
  768. if (IgnoreCase) {
  769. sMatchUpper = wcschr( s, SearchData1UpperCase[ 0 ] );
  770. if (sMatch == NULL ||
  771. (sMatchUpper != NULL && sMatchUpper < sMatch)
  772. ) {
  773. sMatch = sMatchUpper;
  774. }
  775. }
  776. if (sMatch != NULL) {
  777. if ((!IgnoreCase &&
  778. !wcsncmp( sMatch,
  779. (PWSTR)SearchData,
  780. SearchDataLength / sizeof( WCHAR )
  781. )
  782. ) ||
  783. (IgnoreCase &&
  784. !_wcsnicmp( sMatch,
  785. (PWSTR)SearchData,
  786. SearchDataLength / sizeof( WCHAR )
  787. )
  788. )
  789. ) {
  790. MatchFound = TRUE;
  791. s = ApplyReplacementBuffer( &ValueDataLength,
  792. sMatch,
  793. &BufferOverflow
  794. );
  795. if (s == NULL) {
  796. if (BufferOverflow) {
  797. fprintf( stderr,
  798. "REGFIND: Buffer overflow doing replacement",
  799. Error
  800. );
  801. }
  802. break;
  803. }
  804. ReplacementMade = TRUE;
  805. sEnd = (PWSTR)((PCHAR)sBegin + ValueDataLength);
  806. }
  807. else {
  808. s = sMatch + 1;
  809. if (*s == UNICODE_NULL &&
  810. ValueType != REG_MULTI_SZ
  811. ) {
  812. s += 1;
  813. }
  814. }
  815. }
  816. else {
  817. if (ValueType != REG_MULTI_SZ) {
  818. break;
  819. }
  820. while (*s++) {
  821. }
  822. }
  823. }
  824. }
  825. }
  826. else
  827. if (ValueType == REG_DWORD) {
  828. if (*(PULONG)SearchData == *(PULONG)OldValueBuffer) {
  829. MatchFound = TRUE;
  830. }
  831. }
  832. else
  833. if (ValueType == REG_QWORD) {
  834. if (*(PDWORDLONG)SearchData == *(PDWORDLONG)OldValueBuffer) {
  835. MatchFound = TRUE;
  836. }
  837. }
  838. else
  839. if (ValueType == REG_BINARY) {
  840. if (SearchingForMatchOnDataLength) {
  841. if (ValueDataLength == SearchDataLength) {
  842. MatchFound = TRUE;
  843. }
  844. }
  845. else {
  846. sBegin = OldValueBuffer;
  847. sEnd = (PWSTR)((PCHAR)sBegin + ValueDataLength);
  848. s = sBegin;
  849. while (((PCHAR)s + SearchDataLength) < (PCHAR)sEnd) {
  850. s = memchr( s,
  851. *(PBYTE)SearchData,
  852. (UINT)((PCHAR)sEnd - (PCHAR)s)
  853. );
  854. if (s != NULL) {
  855. if ((ULONG)((PCHAR)sEnd - (PCHAR)s) >= SearchDataLength &&
  856. !memcmp( s, SearchData, SearchDataLength )
  857. ) {
  858. MatchFound = TRUE;
  859. s = ApplyReplacementBuffer( &ValueDataLength,
  860. s,
  861. &BufferOverflow
  862. );
  863. if (s == NULL) {
  864. if (BufferOverflow) {
  865. fprintf( stderr,
  866. "REGFIND: Buffer overflow doing replacement",
  867. Error
  868. );
  869. }
  870. break;
  871. }
  872. ReplacementMade = TRUE;
  873. sEnd = (PWSTR)((PCHAR)sBegin + ValueDataLength);
  874. }
  875. s = (PWSTR)((PCHAR)s + 1);
  876. }
  877. else {
  878. break;
  879. }
  880. }
  881. }
  882. }
  883. if (ReplacementMade) {
  884. if (ReplaceSZwithEXPAND_SZ && ValueType == REG_SZ) {
  885. ValueType = REG_EXPAND_SZ;
  886. }
  887. Error = RTSetValueKey( &RegistryContext,
  888. KeyHandle,
  889. ValueName,
  890. ValueType,
  891. ValueDataLength,
  892. OldValueBuffer
  893. );
  894. if (Error != NO_ERROR) {
  895. fprintf( stderr,
  896. "REGFIND: Error setting replacement value (%u)\n",
  897. Error
  898. );
  899. }
  900. }
  901. }
  902. }
  903. }
  904. if (MatchFound) {
  905. DisplayPath( KeyPathInfo, Depth );
  906. RTFormatKeyValue( OutputWidth,
  907. (PREG_OUTPUT_ROUTINE)fprintf,
  908. stdout,
  909. FALSE,
  910. Depth * IndentMultiple,
  911. ValueName,
  912. ValueDataLength,
  913. ValueType,
  914. OldValueBuffer
  915. );
  916. }
  917. }
  918. except( EXCEPTION_EXECUTE_HANDLER ) {
  919. fprintf( stderr, "REGFIND: Access violation searching value\n" );
  920. }
  921. }
  922. else
  923. if (Error == ERROR_NO_MORE_ITEMS) {
  924. return;
  925. }
  926. else {
  927. if (DebugOutput) {
  928. fprintf( stderr,
  929. "REGFIND: RTEnumerateValueKey( %ws ) failed (%u)\n",
  930. KeyName,
  931. Error
  932. );
  933. }
  934. return;
  935. }
  936. }
  937. }