736 lines
21 KiB
C
736 lines
21 KiB
C
/*++
|
||
|
||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
||
Module Name:
|
||
|
||
pnpreg.c
|
||
|
||
Abstract:
|
||
|
||
This module contains code to performs "locking" and "unlocking" of the Plug
|
||
and Play "Enum" branch in the registry. It is for development purposes
|
||
only.
|
||
|
||
The tool "locks" the Enum branch by granting Full Control of the Enum key
|
||
and all subkeys to LocalSystem only. All others are granted Read access,
|
||
except for the "Device Parameters" subkeys of each instance key, to which
|
||
Admins are always granted Full Control. This is the default security
|
||
configuration on the Enum branch.
|
||
|
||
The tool "unlocks" the Enum branch by granting Full Control to
|
||
Administrators and LocalSystem to all subkeys. Effectively, all Enum
|
||
subkeys have the same permissions as the "Device Parameters" key. This mode
|
||
lowers the barrier for users to make changes to the registry directly,
|
||
rather than by the Plug and Play manager. This configuration level should
|
||
not be maintained on a running system for any length of time.
|
||
|
||
Author:
|
||
|
||
Robert B. Nelson (robertn) 10-Feb-1998
|
||
|
||
Revision History:
|
||
|
||
10-Feb-1998 Robert B. Nelson (robertn)
|
||
|
||
Creation and initial implementation.
|
||
|
||
17-Apr-2002 James G. Cavalaris (jamesca)
|
||
|
||
Modified ACLs to reflect current Enum branch permissions.
|
||
|
||
--*/
|
||
|
||
#include <windows.h>
|
||
#include <stdio.h>
|
||
#include <tchar.h>
|
||
#include <aclapi.h>
|
||
#include <regstr.h>
|
||
#include <strsafe.h>
|
||
|
||
|
||
PSID g_pWorldSid;
|
||
PSID g_pAdminSid;
|
||
PSID g_pSystemSid;
|
||
|
||
SECURITY_DESCRIPTOR g_DeviceParametersSD;
|
||
PACL g_pDeviceParametersDacl;
|
||
|
||
SECURITY_DESCRIPTOR g_LockedPrivateKeysSD;
|
||
PACL g_pLockedPrivateKeysDacl;
|
||
|
||
|
||
#if DBG || UMODETEST
|
||
#define DBGF_ERRORS 0x00000001
|
||
#define DBGF_WARNINGS 0x00000002
|
||
|
||
#define DBGF_REGISTRY 0x00000010
|
||
|
||
void RegFixDebugMessage(LPTSTR format, ...);
|
||
#define DBGTRACE(l, x) (g_RegFixDebugFlag & (l) ? RegFixDebugMessage x : (void)0)
|
||
|
||
DWORD g_RegFixDebugFlag = DBGF_WARNINGS | DBGF_ERRORS;
|
||
|
||
TCHAR g_szCurrentKeyName[4096];
|
||
DWORD g_dwCurrentKeyNameLength = 0;
|
||
|
||
#else
|
||
#define DBGTRACE(l, x)
|
||
#endif
|
||
|
||
|
||
|
||
VOID
|
||
FreeSecurityDescriptors(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function deallocates the data structures allocated and initialized by
|
||
CreateDeviceParametersSD.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
if (g_pDeviceParametersDacl != NULL) {
|
||
LocalFree(g_pDeviceParametersDacl);
|
||
g_pDeviceParametersDacl = NULL;
|
||
}
|
||
|
||
if (g_pLockedPrivateKeysDacl != NULL) {
|
||
LocalFree(g_pLockedPrivateKeysDacl);
|
||
g_pLockedPrivateKeysDacl = NULL;
|
||
}
|
||
|
||
if (g_pAdminSid != NULL) {
|
||
FreeSid(g_pAdminSid);
|
||
g_pAdminSid = NULL;
|
||
}
|
||
|
||
if (g_pWorldSid != NULL) {
|
||
FreeSid(g_pWorldSid);
|
||
g_pWorldSid = NULL;
|
||
}
|
||
|
||
if (g_pSystemSid != NULL) {
|
||
FreeSid(g_pSystemSid);
|
||
g_pSystemSid = NULL;
|
||
}
|
||
|
||
return;
|
||
|
||
} // FreeSecurityDescriptors
|
||
|
||
|
||
|
||
BOOL
|
||
CreateSecurityDescriptors(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function creates a properly initialized Security Descriptor for the
|
||
Device Parameters key and its subkeys. The SIDs and DACL created by this
|
||
routine must be freed by calling FreeDeviceParametersSD.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
Returns TRUE if all required security descriptors were successfully created,
|
||
otherwise returns FALSE.
|
||
|
||
--*/
|
||
|
||
{
|
||
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
|
||
SID_IDENTIFIER_AUTHORITY WorldAuthority = SECURITY_WORLD_SID_AUTHORITY;
|
||
|
||
EXPLICIT_ACCESS ExplicitAccess[3];
|
||
|
||
DWORD dwError;
|
||
BOOL bSuccess;
|
||
|
||
DWORD i;
|
||
|
||
//
|
||
// Create SIDs - Admins and System
|
||
//
|
||
|
||
bSuccess = AllocateAndInitializeSid( &NtAuthority,
|
||
2,
|
||
SECURITY_BUILTIN_DOMAIN_RID,
|
||
DOMAIN_ALIAS_RID_ADMINS,
|
||
0, 0, 0, 0, 0, 0,
|
||
&g_pAdminSid);
|
||
|
||
bSuccess = bSuccess && AllocateAndInitializeSid( &NtAuthority,
|
||
1,
|
||
SECURITY_LOCAL_SYSTEM_RID,
|
||
0, 0, 0, 0, 0, 0, 0,
|
||
&g_pSystemSid);
|
||
|
||
bSuccess = bSuccess && AllocateAndInitializeSid( &WorldAuthority,
|
||
1,
|
||
SECURITY_WORLD_RID,
|
||
0, 0, 0, 0, 0, 0, 0,
|
||
&g_pWorldSid);
|
||
|
||
if (bSuccess) {
|
||
|
||
//
|
||
// Initialize Access structures describing the ACEs we want:
|
||
// System Full Control
|
||
// World Read
|
||
// Admins Full Control
|
||
//
|
||
// We'll take advantage of the fact that the unlocked private keys is
|
||
// the same as the device parameters key and they are a superset of the
|
||
// locked private keys.
|
||
//
|
||
// When we create the DACL for the private key we'll specify a subset of
|
||
// the ExplicitAccess array.
|
||
//
|
||
|
||
for (i = 0; i < 3; i++) {
|
||
ExplicitAccess[i].grfAccessMode = SET_ACCESS;
|
||
ExplicitAccess[i].grfInheritance = CONTAINER_INHERIT_ACE;
|
||
ExplicitAccess[i].Trustee.pMultipleTrustee = NULL;
|
||
ExplicitAccess[i].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
|
||
ExplicitAccess[i].Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
||
ExplicitAccess[i].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
|
||
}
|
||
|
||
ExplicitAccess[0].grfAccessPermissions = KEY_ALL_ACCESS;
|
||
ExplicitAccess[0].Trustee.ptstrName = (LPTSTR)g_pSystemSid;
|
||
|
||
ExplicitAccess[1].grfAccessPermissions = KEY_READ;
|
||
ExplicitAccess[1].Trustee.ptstrName = (LPTSTR)g_pWorldSid;
|
||
|
||
ExplicitAccess[2].grfAccessPermissions = KEY_ALL_ACCESS;
|
||
ExplicitAccess[2].Trustee.ptstrName = (LPTSTR)g_pAdminSid;
|
||
|
||
//
|
||
// Create the DACL with all of the above ACEs for the DeviceParameters
|
||
//
|
||
dwError = SetEntriesInAcl( 3,
|
||
ExplicitAccess,
|
||
NULL,
|
||
&g_pDeviceParametersDacl );
|
||
|
||
if (dwError == ERROR_SUCCESS) {
|
||
//
|
||
// Create the DACL with just the system and world ACEs for the
|
||
// locked private keys.
|
||
//
|
||
dwError = SetEntriesInAcl( 2,
|
||
ExplicitAccess,
|
||
NULL,
|
||
&g_pLockedPrivateKeysDacl );
|
||
}
|
||
|
||
bSuccess = dwError == ERROR_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Initialize the DeviceParameters security descriptor
|
||
//
|
||
bSuccess = bSuccess && InitializeSecurityDescriptor( &g_DeviceParametersSD,
|
||
SECURITY_DESCRIPTOR_REVISION );
|
||
|
||
//
|
||
// Set the new DACL in the security descriptor
|
||
//
|
||
bSuccess = bSuccess && SetSecurityDescriptorDacl( &g_DeviceParametersSD,
|
||
TRUE,
|
||
g_pDeviceParametersDacl,
|
||
FALSE);
|
||
|
||
//
|
||
// validate the new security descriptor
|
||
//
|
||
bSuccess = bSuccess && IsValidSecurityDescriptor( &g_DeviceParametersSD );
|
||
|
||
|
||
//
|
||
// Initialize the DeviceParameters security descriptor
|
||
//
|
||
bSuccess = bSuccess && InitializeSecurityDescriptor( &g_LockedPrivateKeysSD,
|
||
SECURITY_DESCRIPTOR_REVISION );
|
||
|
||
//
|
||
// Set the new DACL in the security descriptor
|
||
//
|
||
bSuccess = bSuccess && SetSecurityDescriptorDacl( &g_LockedPrivateKeysSD,
|
||
TRUE,
|
||
g_pLockedPrivateKeysDacl,
|
||
FALSE);
|
||
|
||
//
|
||
// validate the new security descriptor
|
||
//
|
||
bSuccess = bSuccess && IsValidSecurityDescriptor( &g_LockedPrivateKeysSD );
|
||
|
||
|
||
if (!bSuccess) {
|
||
FreeSecurityDescriptors();
|
||
}
|
||
|
||
return bSuccess;
|
||
|
||
} // CreateSecurityDescriptors
|
||
|
||
|
||
|
||
VOID
|
||
EnumKeysAndApplyDacls(
|
||
IN HKEY hParentKey,
|
||
IN LPTSTR pszKeyName,
|
||
IN DWORD dwLevel,
|
||
IN BOOL bInDeviceParameters,
|
||
IN BOOL bApplyTopDown,
|
||
IN PSECURITY_DESCRIPTOR pPrivateKeySD,
|
||
IN PSECURITY_DESCRIPTOR pDeviceParametersSD
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function applies the DACL in pSD to all the keys rooted at hKey
|
||
including hKey itself.
|
||
|
||
Arguments:
|
||
|
||
hParentKey Handle to a registry key.
|
||
pszKeyName Name of the key.
|
||
dwLevel Number of levels remaining to recurse.
|
||
pSD Pointer to a security descriptor containing a DACL.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
LONG RegStatus;
|
||
DWORD dwMaxSubKeySize;
|
||
LPTSTR pszSubKey;
|
||
DWORD index;
|
||
HKEY hKey;
|
||
BOOL bNewInDeviceParameters;
|
||
|
||
#if DBG || UMODETEST
|
||
DWORD dwStartKeyNameLength = g_dwCurrentKeyNameLength;
|
||
|
||
if (g_dwCurrentKeyNameLength != 0) {
|
||
g_szCurrentKeyName[ g_dwCurrentKeyNameLength++ ] = TEXT('\\');
|
||
}
|
||
|
||
if (SUCCEEDED(StringCchCopy(
|
||
&g_szCurrentKeyName[g_dwCurrentKeyNameLength],
|
||
(sizeof(g_szCurrentKeyName) / sizeof(g_szCurrentKeyName[0])) -
|
||
g_dwCurrentKeyNameLength,
|
||
pszKeyName))) {
|
||
g_dwCurrentKeyNameLength += (DWORD)_tcslen(pszKeyName);
|
||
}
|
||
|
||
#endif
|
||
|
||
DBGTRACE( DBGF_REGISTRY,
|
||
(TEXT("EnumKeysAndApplyDacls(0x%08X, \"%s\", %d, %s, %s, 0x%08X, 0x%08X)\n"),
|
||
hParentKey,
|
||
g_szCurrentKeyName,
|
||
dwLevel,
|
||
bInDeviceParameters ? TEXT("TRUE") : TEXT("FALSE"),
|
||
bApplyTopDown ? TEXT("TRUE") : TEXT("FALSE"),
|
||
pPrivateKeySD,
|
||
pDeviceParametersSD) );
|
||
|
||
if (bApplyTopDown) {
|
||
|
||
RegStatus = RegOpenKeyEx( hParentKey,
|
||
pszKeyName,
|
||
0,
|
||
WRITE_DAC,
|
||
&hKey
|
||
);
|
||
|
||
if (RegStatus != ERROR_SUCCESS) {
|
||
DBGTRACE( DBGF_ERRORS,
|
||
(TEXT("EnumKeysAndApplyDacls(\"%s\") RegOpenKeyEx() failed, error = %d\n"),
|
||
g_szCurrentKeyName, RegStatus));
|
||
|
||
return;
|
||
}
|
||
|
||
DBGTRACE( DBGF_REGISTRY,
|
||
(TEXT("Setting security on %s on the way down\n"),
|
||
g_szCurrentKeyName) );
|
||
|
||
//
|
||
// apply the new security to the registry key
|
||
//
|
||
RegStatus = RegSetKeySecurity( hKey,
|
||
DACL_SECURITY_INFORMATION,
|
||
bInDeviceParameters ?
|
||
pDeviceParametersSD :
|
||
pPrivateKeySD
|
||
);
|
||
|
||
if (RegStatus != ERROR_SUCCESS) {
|
||
DBGTRACE( DBGF_ERRORS,
|
||
(TEXT("EnumKeysAndApplyDacls(\"%s\") RegSetKeySecurity() failed, error = %d\n"),
|
||
g_szCurrentKeyName, RegStatus));
|
||
}
|
||
|
||
//
|
||
// Close the key and reopen it later for read (which hopefully was just
|
||
// granted in the DACL we just wrote
|
||
//
|
||
RegCloseKey( hKey );
|
||
}
|
||
|
||
RegStatus = RegOpenKeyEx( hParentKey,
|
||
pszKeyName,
|
||
0,
|
||
KEY_READ | WRITE_DAC,
|
||
&hKey
|
||
);
|
||
|
||
if (RegStatus != ERROR_SUCCESS) {
|
||
DBGTRACE( DBGF_ERRORS,
|
||
(TEXT("EnumKeysAndApplyDacls(\"%s\") RegOpenKeyEx() failed, error = %d\n"),
|
||
g_szCurrentKeyName, RegStatus));
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Determine length of longest subkey
|
||
//
|
||
RegStatus = RegQueryInfoKey( hKey,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&dwMaxSubKeySize,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL );
|
||
|
||
if (RegStatus == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// Allocate a buffer to hold the subkey names. RegQueryInfoKey returns the
|
||
// size in characters and doesn't include the NUL terminator.
|
||
//
|
||
pszSubKey = LocalAlloc(0, ++dwMaxSubKeySize * sizeof(TCHAR));
|
||
|
||
if (pszSubKey != NULL) {
|
||
|
||
//
|
||
// Enumerate all the subkeys and then call ourselves recursively for each
|
||
// until dwLevel reaches 0.
|
||
//
|
||
|
||
for (index = 0; ; index++) {
|
||
|
||
RegStatus = RegEnumKey( hKey,
|
||
index,
|
||
pszSubKey,
|
||
dwMaxSubKeySize
|
||
);
|
||
|
||
if (RegStatus != ERROR_SUCCESS) {
|
||
|
||
if (RegStatus != ERROR_NO_MORE_ITEMS) {
|
||
|
||
DBGTRACE( DBGF_ERRORS,
|
||
(TEXT("EnumKeysAndApplyDacls(\"%s\") RegEnumKeyEx() failed, error = %d\n"),
|
||
g_szCurrentKeyName,
|
||
RegStatus) );
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
bNewInDeviceParameters = bInDeviceParameters ||
|
||
(dwLevel == 3 &&
|
||
_tcsicmp( pszSubKey,
|
||
REGSTR_KEY_DEVICEPARAMETERS ) == 0);
|
||
|
||
EnumKeysAndApplyDacls( hKey,
|
||
pszSubKey,
|
||
dwLevel + 1,
|
||
bNewInDeviceParameters,
|
||
bApplyTopDown,
|
||
pPrivateKeySD,
|
||
pDeviceParametersSD
|
||
);
|
||
}
|
||
|
||
LocalFree( pszSubKey );
|
||
}
|
||
}
|
||
else
|
||
{
|
||
DBGTRACE( DBGF_ERRORS,
|
||
(TEXT("EnumKeysAndApplyDacls(\"%s\") RegQueryInfoKey() failed, error = %d\n"),
|
||
g_szCurrentKeyName, RegStatus));
|
||
}
|
||
|
||
if (!bApplyTopDown) {
|
||
|
||
DBGTRACE( DBGF_REGISTRY,
|
||
(TEXT("Setting security on %s on the way back up\n"),
|
||
g_szCurrentKeyName) );
|
||
|
||
//
|
||
// apply the new security to the registry key
|
||
//
|
||
RegStatus = RegSetKeySecurity( hKey,
|
||
DACL_SECURITY_INFORMATION,
|
||
bInDeviceParameters ?
|
||
pDeviceParametersSD :
|
||
pPrivateKeySD
|
||
);
|
||
|
||
if (RegStatus != ERROR_SUCCESS) {
|
||
DBGTRACE( DBGF_ERRORS,
|
||
(TEXT("EnumKeysAndApplyDacls(\"%s\") RegSetKeySecurity() failed, error = %d\n"),
|
||
g_szCurrentKeyName, RegStatus));
|
||
}
|
||
}
|
||
|
||
RegCloseKey( hKey );
|
||
|
||
#if DBG || UMODETEST
|
||
g_dwCurrentKeyNameLength = dwStartKeyNameLength;
|
||
g_szCurrentKeyName[g_dwCurrentKeyNameLength] = TEXT('\0');
|
||
#endif
|
||
|
||
return;
|
||
|
||
} // EnumKeysAndApplyDacls
|
||
|
||
|
||
|
||
VOID
|
||
LockUnlockEnumTree(
|
||
LPTSTR pszMachineName,
|
||
BOOL bLock
|
||
)
|
||
{
|
||
HKEY hParentKey = NULL;
|
||
LONG RegStatus;
|
||
|
||
if (pszMachineName != NULL) {
|
||
|
||
RegStatus = RegConnectRegistry( pszMachineName,
|
||
HKEY_LOCAL_MACHINE,
|
||
&hParentKey );
|
||
|
||
if (RegStatus != ERROR_SUCCESS) {
|
||
DBGTRACE( DBGF_ERRORS,
|
||
(TEXT("Could not connect to remote registry on %s, status = %d\n"),
|
||
pszMachineName,
|
||
RegStatus) );
|
||
return;
|
||
}
|
||
|
||
} else {
|
||
|
||
hParentKey = HKEY_LOCAL_MACHINE;
|
||
}
|
||
|
||
if (CreateSecurityDescriptors()) {
|
||
|
||
EnumKeysAndApplyDacls( hParentKey,
|
||
REGSTR_PATH_SYSTEMENUM,
|
||
0,
|
||
FALSE,
|
||
!bLock,
|
||
bLock ? &g_LockedPrivateKeysSD : &g_DeviceParametersSD,
|
||
&g_DeviceParametersSD
|
||
);
|
||
|
||
FreeSecurityDescriptors();
|
||
}
|
||
|
||
if (pszMachineName != NULL) {
|
||
RegCloseKey(hParentKey);
|
||
}
|
||
|
||
return;
|
||
|
||
} // LockUnlockEnumTree
|
||
|
||
|
||
|
||
#if DBG || UMODETEST
|
||
void
|
||
RegFixDebugMessage(
|
||
LPTSTR format,
|
||
...
|
||
)
|
||
{
|
||
va_list args;
|
||
|
||
va_start(args, format);
|
||
|
||
_vtprintf(format, args);
|
||
|
||
return;
|
||
|
||
} // RegFixDebugMessage
|
||
#endif
|
||
|
||
|
||
|
||
#if UMODETEST
|
||
void
|
||
usage(int argc, TCHAR **argv)
|
||
{
|
||
PTCHAR pszProgram;
|
||
|
||
UNREFERENCED_PARAMETER(argc);
|
||
|
||
if ((pszProgram = _tcsrchr(argv[0], TEXT('\\'))) != NULL) {
|
||
pszProgram++;
|
||
} else {
|
||
pszProgram = argv[0];
|
||
}
|
||
|
||
_tprintf(TEXT("%s: Lock or Unlock PnP Registry (Enum key)\n\n"), pszProgram);
|
||
_tprintf(TEXT("Usage: %s [-m <machine>] -l | -u\n"), pszProgram);
|
||
_tprintf(TEXT(" -m <machine> Remote machine without leading \\\\\n"));
|
||
_tprintf(TEXT(" -l Locks Enum key\n"));
|
||
_tprintf(TEXT(" -u Unlocks Enum key\n\n"));
|
||
_tprintf(TEXT("Note: -m is optional. Only one of -l or -u may be used.\n"));
|
||
return;
|
||
}
|
||
|
||
int __cdecl
|
||
_tmain(int argc, TCHAR **argv)
|
||
{
|
||
LPTSTR pszMachineName = NULL;
|
||
LPTSTR pszArg;
|
||
int idxArg;
|
||
|
||
if ( argc == 1 )
|
||
{
|
||
usage(argc, argv);
|
||
return 0;
|
||
}
|
||
|
||
for (idxArg = 1; idxArg < argc; idxArg++)
|
||
{
|
||
pszArg = argv[ idxArg ];
|
||
|
||
if (*pszArg == '/' || *pszArg == '-')
|
||
{
|
||
pszArg++;
|
||
|
||
while (pszArg != NULL && *pszArg != '\0') {
|
||
|
||
switch (*pszArg)
|
||
{
|
||
case '/': // Ignore these, caused by cmds like /m/l
|
||
pszArg++;
|
||
break;
|
||
|
||
case 'l':
|
||
case 'L':
|
||
pszArg++;
|
||
LockUnlockEnumTree( pszMachineName, TRUE );
|
||
break;
|
||
|
||
case 'm':
|
||
case 'M':
|
||
pszArg++;
|
||
|
||
if (*pszArg == ':' || *pszArg == '=')
|
||
{
|
||
if (pszArg[ 1 ] != '\0')
|
||
{
|
||
pszMachineName = ++pszArg;
|
||
}
|
||
}
|
||
else if (*pszArg != '\0')
|
||
{
|
||
pszMachineName = pszArg;
|
||
}
|
||
else if ((idxArg + 1) < argc && (argv[ idxArg + 1 ][0] != '/' && argv[ idxArg + 1 ][0] != '-'))
|
||
{
|
||
pszMachineName = argv[ ++idxArg ];
|
||
}
|
||
|
||
if (pszMachineName == NULL)
|
||
{
|
||
_tprintf(
|
||
TEXT("%c%c : missing machine name argument\n"),
|
||
argv[ idxArg ][ 0 ], pszArg [ - 1 ]
|
||
);
|
||
|
||
usage(argc, argv);
|
||
|
||
return 1;
|
||
}
|
||
pszArg = NULL;
|
||
break;
|
||
|
||
case 'u':
|
||
case 'U':
|
||
pszArg++;
|
||
|
||
LockUnlockEnumTree( pszMachineName, FALSE );
|
||
break;
|
||
|
||
case 'v':
|
||
case 'V':
|
||
pszArg++;
|
||
|
||
g_RegFixDebugFlag |= DBGF_REGISTRY;
|
||
break;
|
||
|
||
default:
|
||
_tprintf(
|
||
TEXT("%c%c : invalid option\n"),
|
||
argv[ idxArg ][ 0 ], *pszArg
|
||
);
|
||
pszArg++;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
#endif
|
||
|
||
|
||
|