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.

563 lines
17 KiB

  1. /*
  2. Note: This program creates the following files in %systemroot%\system32\config:
  3. system.sc0 Backup at start of operations
  4. system.scc Backup if compression is on (win2k only)
  5. system.scb Backup after removing devnodes (win2k only)
  6. system.scn Temporary during replacement of system (should go away)
  7. system Updated system hive (should match system.scb)
  8. Modification History:
  9. 10/3/2000 original version from jasconc
  10. 10/4/2000 put back compression option, version 0.9
  11. 10/10/2000 if compression (no removals), save backup as .scc, else as .sc0
  12. 10/13/2000 Add version check (Win2K only, not NT4, not Whistler, etc.)
  13. 10/20/2000 Return DevicesRemoved to help with diskpart in scripts
  14. 12/11/2001 Updated headers, check for .NET and added NtCompressKey for inplace registry compression. v1.01
  15. */
  16. #pragma warning( disable : 4201 ) // nonstandard extension used : nameless strut/union
  17. #include <nt.h>
  18. #include <ntrtl.h>
  19. #include <nturtl.h>
  20. #include <windows.h>
  21. #include <stdlib.h>
  22. #include <stdio.h>
  23. #include <stddef.h>
  24. #include <tchar.h>
  25. #include <setupapi.h>
  26. #include <sputils.h>
  27. #include <cfgmgr32.h>
  28. #include <regstr.h>
  29. #include <initguid.h>
  30. #include <devguid.h>
  31. #include <winioctl.h>
  32. #pragma warning( default : 4201 )
  33. #define SIZECHARS(x) (sizeof((x))/sizeof(TCHAR))
  34. CONST GUID *ClassesToClean[2] = {
  35. &GUID_DEVCLASS_DISKDRIVE,
  36. &GUID_DEVCLASS_VOLUME
  37. };
  38. CONST GUID *DeviceInterfacesToClean[5] = {
  39. &DiskClassGuid,
  40. &PartitionClassGuid,
  41. &WriteOnceDiskClassGuid,
  42. &VolumeClassGuid,
  43. &StoragePortClassGuid
  44. };
  45. void PERR(void);
  46. BOOL
  47. IsUserAdmin(
  48. VOID
  49. )
  50. /*++
  51. Routine Description:
  52. This routine returns TRUE if the caller's process is a
  53. member of the Administrators local group.
  54. Caller is NOT expected to be impersonating anyone and IS
  55. expected to be able to open their own process and process
  56. token.
  57. Arguments:
  58. None.
  59. Return Value:
  60. TRUE - Caller has Administrators local group.
  61. FALSE - Caller does not have Administrators local group.
  62. --*/
  63. {
  64. BOOL b;
  65. SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
  66. PSID AdministratorsGroup;
  67. b = AllocateAndInitializeSid(&NtAuthority,
  68. 2,
  69. SECURITY_BUILTIN_DOMAIN_RID,
  70. DOMAIN_ALIAS_RID_ADMINS,
  71. 0,
  72. 0,
  73. 0,
  74. 0,
  75. 0,
  76. 0,
  77. &AdministratorsGroup
  78. );
  79. if (b) {
  80. if (!CheckTokenMembership(NULL,
  81. AdministratorsGroup,
  82. &b
  83. )) {
  84. b = FALSE;
  85. }
  86. FreeSid(AdministratorsGroup);
  87. }
  88. return (b);
  89. }
  90. int
  91. __cdecl
  92. main(
  93. IN int argc,
  94. IN char *argv[]
  95. )
  96. {
  97. HDEVINFO DeviceInfoSet;
  98. HDEVINFO InterfaceDeviceInfoSet;
  99. SP_DEVINFO_DATA DeviceInfoData;
  100. SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
  101. int DevicesRemoved = 0;
  102. int i, InterfaceIndex;
  103. int MemberIndex, InterfaceMemberIndex;
  104. BOOL bDoRemove = TRUE, bDoCompress = FALSE;
  105. DWORD Status, Problem;
  106. CONFIGRET cr;
  107. TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN];
  108. TCHAR DirBuff[MAX_PATH], DirBuffX[MAX_PATH], BackupDir[MAX_PATH];
  109. UINT uPathLen;
  110. HKEY hKey;
  111. DWORD dwMessage;
  112. TOKEN_PRIVILEGES tp;
  113. HANDLE hToken;
  114. LUID luid;
  115. LPTSTR MachineName=NULL; // pointer to machine name
  116. OSVERSIONINFO osvi;
  117. BOOL bWindows2000 = TRUE;
  118. FARPROC CompressKey = NULL;
  119. HANDLE hModule = NULL;
  120. //
  121. // enable backup privilege at least
  122. //
  123. printf("SCRUBBER 1.01 Storage Device Node Cleanup\nCopyright (c) Microsoft Corp. All rights reserved.\n");
  124. //
  125. // parse parameters.
  126. //
  127. for (i = 1; i < argc; i++) {
  128. //
  129. // Check for help
  130. //
  131. if ( (lstrcmpi(argv[i], TEXT("-?")) == 0) ||
  132. (lstrcmpi(argv[i], TEXT("/?")) == 0) ){
  133. printf("\nSCRUBBER will remove phantom storage device nodes from this machine.\n\n");
  134. printf("Usage: scrubber [/n] [/c]\n");
  135. printf("\twhere /n displays but does not remove the phantom devnodes.\n");
  136. printf("\t and /c will compress the registry hive even if no changes are made.\n");
  137. printf("\nBackup and Restore privileges are required to run this utility.\n");
  138. printf("A copy of the registry will saved in %%systemroot%%\\system32\\config\\system.sc0\n");
  139. return 0;
  140. }
  141. //
  142. // Check for -n which means just list the devices that
  143. // we will remove.
  144. //
  145. if ( (lstrcmpi(argv[i], TEXT("-n")) == 0) ||
  146. (lstrcmpi(argv[i], TEXT("/n")) == 0) ) {
  147. bDoRemove = FALSE;
  148. }
  149. //
  150. // Force compress mode?
  151. //
  152. if ( (lstrcmpi(argv[i], TEXT("-c")) == 0) ||
  153. (lstrcmpi(argv[i], TEXT("/c")) == 0) ){
  154. bDoCompress = TRUE;
  155. }
  156. }
  157. //
  158. // Only run on Windows 2000 (not XP, etc.) Initialize the OSVERSIONINFOEX structure.
  159. //
  160. //
  161. // Only run on Windows 2000 (version 5.0) and Windows XP/.NET server (version 5.1).
  162. //
  163. ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
  164. osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  165. if (!GetVersionEx(&osvi)) {
  166. fprintf(stderr, "SCRUBBER: Unable to verify Windows version, exiting...\n");
  167. return -1;
  168. }
  169. if ( (osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 0) ) {
  170. bWindows2000 = TRUE;
  171. }
  172. else if ((osvi.dwMajorVersion == 5) &&
  173. ((osvi.dwMinorVersion == 1) || (osvi.dwMinorVersion == 2))) {
  174. bWindows2000 = FALSE;
  175. hModule = LoadLibrary(TEXT("ntdll.dll"));
  176. CompressKey = GetProcAddress(hModule, TEXT("NtCompressKey"));
  177. }
  178. else
  179. {
  180. fprintf(stderr, "SCRUBBER: This utility is only designed to run on Windows 2000/XP/.NET server\n");
  181. return -1;
  182. }
  183. //
  184. // The process must have admin credentials.
  185. //
  186. if (!IsUserAdmin()) {
  187. fprintf(stderr, "SCRUBBER: You must be an administrator to run this utility.\n");
  188. return -1;
  189. }
  190. //
  191. // see if we can do the task, need backup privelege.
  192. //
  193. if(!OpenProcessToken(GetCurrentProcess(),
  194. TOKEN_ADJUST_PRIVILEGES,
  195. &hToken )) {
  196. fprintf(stderr, "SCRUBBER: Unable to obtain process token.\nCheck privileges.\n");
  197. return -1;
  198. }
  199. if(!LookupPrivilegeValue(MachineName, SE_BACKUP_NAME, &luid)) {
  200. fprintf(stderr, "SCRUBBER: Backup Privilege is required to save the registry.\n"
  201. "Please rerun from a privileged account\n");
  202. return -1;
  203. }
  204. tp.PrivilegeCount = 1;
  205. tp.Privileges[0].Luid = luid;
  206. tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  207. if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES),
  208. NULL, NULL )) {
  209. fprintf(stderr, "SCRUBBER: Unable to set Backup Privilege.\n");
  210. return -1;
  211. }
  212. //
  213. // Backup the file if we aren't doing a dry run
  214. //
  215. if ( bDoCompress || bDoRemove)
  216. {
  217. if(!LookupPrivilegeValue(MachineName, SE_RESTORE_NAME, &luid))
  218. if(!LookupPrivilegeValue(MachineName, SE_RESTORE_NAME, &luid)) {
  219. fprintf(stderr, "SCRUBBER: Restore Privilege is required to make changes to the registry.\n"
  220. "Please rerun from a privileged account\n");
  221. return -1;
  222. }
  223. tp.Privileges[0].Luid = luid;
  224. AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES),
  225. NULL, NULL );
  226. if (GetLastError() != ERROR_SUCCESS) {
  227. fprintf(stderr, "SCRUBBER: Unable to set Restore Privilege.\n");
  228. return -1;
  229. }
  230. if (RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("System"), &hKey) == ERROR_SUCCESS) {
  231. uPathLen = GetSystemDirectory(DirBuffX, SIZECHARS(DirBuffX));
  232. if (uPathLen < SIZECHARS(DirBuffX) - strlen(TEXT("\\config\\system.scx")) - 1) {
  233. strcat(DirBuffX, TEXT("\\config\\system.scx"));
  234. dwMessage = RegSaveKey(hKey, DirBuffX, NULL);
  235. if (dwMessage == ERROR_ALREADY_EXISTS) {
  236. DeleteFile(DirBuffX);
  237. dwMessage = RegSaveKey(hKey, DirBuffX, NULL);
  238. }
  239. RegCloseKey(hKey);
  240. if (dwMessage != ERROR_SUCCESS) {
  241. fprintf(stderr, "Unable to save a backup copy of the system hive.\n"
  242. "No changes have been made.\n\n");
  243. return -1;
  244. }
  245. }
  246. }
  247. }
  248. for (i=0; (i<sizeof(ClassesToClean)) && (bDoRemove || !bDoCompress); i++) {
  249. DeviceInfoSet = SetupDiGetClassDevs(ClassesToClean[i],
  250. NULL,
  251. NULL,
  252. 0
  253. );
  254. if (DeviceInfoSet != INVALID_HANDLE_VALUE) {
  255. DeviceInfoData.cbSize = sizeof(DeviceInfoData);
  256. MemberIndex = 0;
  257. while (SetupDiEnumDeviceInfo(DeviceInfoSet,
  258. MemberIndex++,
  259. &DeviceInfoData
  260. )) {
  261. //
  262. // Check if this device is a Phantom
  263. //
  264. cr = CM_Get_DevNode_Status(&Status,
  265. &Problem,
  266. DeviceInfoData.DevInst,
  267. 0
  268. );
  269. if ((cr == CR_NO_SUCH_DEVINST) ||
  270. (cr == CR_NO_SUCH_VALUE)) {
  271. //
  272. // This is a phantom. Now get the DeviceInstanceId so we
  273. // can display this as output.
  274. //
  275. if (CM_Get_Device_ID(DeviceInfoData.DevInst,
  276. DeviceInstanceId,
  277. SIZECHARS(DeviceInstanceId),
  278. 0) == CR_SUCCESS) {
  279. if (bDoRemove) {
  280. printf("SCRUBBER: %s will be removed.\n",
  281. DeviceInstanceId
  282. );
  283. //
  284. // On Windows 2000 DIF_REMOVE doesn't always clean
  285. // out all of the device's interfaces for RAW
  286. // devnodes. Because of this we need to manually
  287. // build up a list of device interfaces that we care
  288. // about that are associated with this DeviceInfoData
  289. // and manually remove them.
  290. //
  291. if (bWindows2000) {
  292. for (InterfaceIndex = 0;
  293. InterfaceIndex < sizeof(DeviceInterfacesToClean);
  294. InterfaceIndex++) {
  295. //
  296. // Build up a list of the interfaces for this specific
  297. // device.
  298. //
  299. InterfaceDeviceInfoSet =
  300. SetupDiGetClassDevs(DeviceInterfacesToClean[InterfaceIndex],
  301. DeviceInstanceId,
  302. NULL,
  303. DIGCF_DEVICEINTERFACE
  304. );
  305. if (InterfaceDeviceInfoSet != INVALID_HANDLE_VALUE) {
  306. //
  307. // Enumerate through the interfaces that we just
  308. // built up.
  309. //
  310. DeviceInterfaceData.cbSize = sizeof(DeviceInterfaceData);
  311. InterfaceMemberIndex = 0;
  312. while (SetupDiEnumDeviceInterfaces(InterfaceDeviceInfoSet,
  313. NULL,
  314. DeviceInterfacesToClean[InterfaceIndex],
  315. InterfaceMemberIndex++,
  316. &DeviceInterfaceData
  317. )) {
  318. //
  319. // Remove this Interface from the registry.
  320. //
  321. SetupDiRemoveDeviceInterface(InterfaceDeviceInfoSet,
  322. &DeviceInterfaceData
  323. );
  324. }
  325. //
  326. // Destroy the list of Interfaces that we built up.
  327. //
  328. SetupDiDestroyDeviceInfoList(InterfaceDeviceInfoSet);
  329. }
  330. }
  331. }
  332. //
  333. // Call DIF_REMOVE to remove the device's hardware
  334. // and software registry keys.
  335. //
  336. if (SetupDiCallClassInstaller(DIF_REMOVE,
  337. DeviceInfoSet,
  338. &DeviceInfoData
  339. )) {
  340. DevicesRemoved++;
  341. } else {
  342. fprintf(stderr, "SCRUBBER: Error 0x%X removing phantom\n",
  343. GetLastError());
  344. }
  345. } else {
  346. printf("SCRUBBER: %s would have been removed.\n",
  347. DeviceInstanceId
  348. );
  349. }
  350. }
  351. }
  352. }
  353. SetupDiDestroyDeviceInfoList(DeviceInfoSet);
  354. }
  355. }
  356. //
  357. // Compress registry now
  358. //
  359. if (DevicesRemoved || bDoCompress) {
  360. uPathLen = GetSystemDirectory(DirBuff, SIZECHARS(DirBuff));
  361. SetLastError(0);
  362. if (uPathLen < SIZECHARS(DirBuff) - strlen(TEXT("\\config\\system.scn")) - 1) {
  363. //
  364. // Rename our backup copy
  365. //
  366. if (!DevicesRemoved) {
  367. strcat(DirBuff, TEXT("\\config\\system.scc"));
  368. } else {
  369. strcat(DirBuff, TEXT("\\config\\system.sc0"));
  370. }
  371. DeleteFile(DirBuff);
  372. if (rename(DirBuffX, DirBuff)) {
  373. fprintf(stderr, "SCRUBBER: Failed to rename backup file (system.scx)\n");
  374. } else {
  375. printf("System hive backup saved in %s\n", DirBuff);
  376. }
  377. } else {
  378. fprintf(stderr, "SCRUBBER: Path name too long. Registry not compressed.\n");
  379. }
  380. if (RegOpenKey(HKEY_LOCAL_MACHINE, "System", &hKey) == ERROR_SUCCESS)
  381. {
  382. if (bWindows2000)
  383. {
  384. // Make an additional copy now because it gets blown away on replace
  385. uPathLen = GetSystemDirectory(DirBuff, sizeof(DirBuff));
  386. strcat(DirBuff, "\\config\\system.scn");
  387. dwMessage = RegSaveKey(hKey, DirBuff, NULL);
  388. if (dwMessage == ERROR_ALREADY_EXISTS)
  389. {
  390. DeleteFile(DirBuff);
  391. dwMessage = RegSaveKey(hKey, DirBuff, NULL);
  392. }
  393. if (dwMessage == ERROR_SUCCESS)
  394. {
  395. if (bDoCompress) {
  396. TCHAR *tcPtr;
  397. sprintf(BackupDir, DirBuff);
  398. tcPtr = strstr(BackupDir, ".scn");
  399. strcpy(tcPtr, ".scb");
  400. if (!DeleteFile(BackupDir))
  401. {
  402. dwMessage = GetLastError();
  403. }
  404. dwMessage = RegReplaceKey(hKey, NULL, DirBuff, BackupDir);
  405. if (dwMessage != ERROR_SUCCESS)
  406. PERR();
  407. else
  408. printf("Saved new system hive.\n");
  409. }
  410. }
  411. else
  412. {
  413. PERR();
  414. }
  415. }
  416. else // XP/.NET
  417. {
  418. if ((CompressKey)(hKey) == ERROR_SUCCESS)
  419. {
  420. printf("Compressed system hive.\n");
  421. }
  422. else
  423. {
  424. PERR();
  425. }
  426. FreeLibrary(hModule);
  427. }
  428. RegCloseKey(hKey);
  429. }
  430. } else {
  431. DeleteFile(DirBuffX);
  432. }
  433. return DevicesRemoved;
  434. }
  435. void PERR(void)
  436. {
  437. LPVOID lpMsgBuf;
  438. FormatMessage(
  439. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  440. FORMAT_MESSAGE_FROM_SYSTEM |
  441. FORMAT_MESSAGE_IGNORE_INSERTS,
  442. NULL,
  443. GetLastError(),
  444. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  445. (LPTSTR) &lpMsgBuf,
  446. 0,
  447. NULL);
  448. fprintf(stderr, lpMsgBuf);
  449. LocalFree(lpMsgBuf);
  450. }