windows 是否可以使用不同的凭据读取/写入远程计算机的注册表?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5972352/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 16:50:22  来源:igfitidea点击:

Is it possible to read/write the registry of a remote machine with different credentials?

windowsdelphiwinapi

提问by Mick

I am using Delphi to remotely read and write the registry of a remote machine. This works when my account on my machine has admin access to the remote machine.

我正在使用 Delphi 远程读取和写入远程机器的注册表。当我的机器上的帐户对远程机器具有管理员访问权限时,这会起作用。

However, I'd like to be able to specify a username / pwd when connecting to read the registry so I can connect with alternate credentials.

但是,我希望能够在连接读取注册表时指定用户名/密码,以便我可以使用备用凭据进行连接。

With the file-system I called the following (with the username and password) and was able to establish a connection to the remote system and perform filesystem related functions. However, this does not appear to work with the registry.

使用文件系统,我调用了以下(使用用户名和密码)并能够建立到远程系统的连接并执行文件系统相关功能。但是,这似乎不适用于注册表。

var
  netResource       : TNetResource;
begin

  FillChar(netResource, SizeOf(netResource), 0);
  netResource.dwScope := RESOURCE_GLOBALNET;
  netResource.dwType := RESOURCETYPE_DISK;
  netResource.dwDisplayType := RESOURCEDISPLAYTYPE_SHARE;
  netResource.dwUsage := RESOURCEUSAGE_CONNECTABLE;
  netResource.lpRemoteName := PChar('\192.168.1.105\IPC$');

  WNetAddConnection2(netResource, PChar(password), PChar(username), 0);
end;

...

...

And here is the example of the function I'd like to be able to call, but specify the credentials with access to the remote machine:

这是我希望能够调用的函数的示例,但指定可以访问远程机器的凭据:

procedure TForm1.SetWallpaperKey() ;
var
   reg:TRegistry;
begin
   reg:=TRegistry.Create;

   with reg do begin
    try
     if RegistryConnect('192.168.1.105') then
       if OpenKey('\Control Panel\desktop', False) then begin
       //change wallpaper and tile it
        reg.WriteString ('Wallpaper','c:\windows\CIRCLES.bmp') ;
        reg.WriteString ('TileWallpaper','1') ;
        //disable screen saver//('0'=disable, '1'=enable)
        reg.WriteString('ScreenSaveActive','0') ;
       end
    finally
      reg.Free;
    end;
   end;
end;

回答by RRUZ

Mick , i can't resist give you a WMI solution for you problem ;) , the wmi have a class called StdRegProvwhich allow you to access the registry in local and remote machines. A key point is the namespace where the class is located, that depends of the version of windows installed in the remote machine. so for Windows Server 2003, Windows XP, Windows 2000, Windows NT 4.0, and Windows Me/98/95 the StdRegProvclass is available in the root\defaultnamespace and for others versions like windows Vista/7 the namespace is root\CIMV2.

米克,我忍不住为你的问题提供了一个 WMI 解决方案;),wmi 有一个名为的类StdRegProv,它允许你访问本地和远程机器上的注册表。一个关键点是类所在的命名空间,这取决于远程机器上安装的 Windows 版本。因此,对于 Windows Server 2003、Windows XP、Windows 2000、Windows NT 4.0 和 Windows Me/98/95, StdRegProv该类在root\default命名空间中可用,而对于 Windows Vista/7 等其他版本,命名空间为root\CIMV2.

Now to configure the credentials to access the registry, you must set these values in the SWbemLocator.ConnectServermethod in this way :

现在要配置访问注册表的凭据,您必须SWbemLocator.ConnectServer以这种方式在方法中设置这些值:

FSWbemLocator.ConnectServer(Server, 'root\default', User, Pass);

Another impor point is which this class just exposes methods to access the registry not properties, so you cannot use a wmi query, instead you must execute wmi methods.

另一个重要点是该类只公开访问注册表的方法而不是属性,因此您不能使用 wmi 查询,而必须执行 wmi 方法。

check the next samples to see how it works.

检查下一个示例以了解它是如何工作的。

checking if you have permissions over a key

检查您是否对密钥具有权限

uses
  Windows,
  SysUtils,
  ActiveX,
  ComObj;

// The CheckAccess method verifies that the user possesses the specified
// permissions. The method returns a uint32 which is 0 if successful or some other
// value if any other error occurred.

procedure  Invoke_StdRegProv_CheckAccess;
const
  Server = '192.168.52.128';
  User   = 'Administrator';
  Pass   = 'password';
var
  FSWbemLocator   : OLEVariant;
  FWMIService     : OLEVariant;
  FWbemObjectSet  : OLEVariant;
  FInParams       : OLEVariant;
  FOutParams      : OLEVariant;
begin
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  //http://msdn.microsoft.com/en-us/library/aa393664%28v=vs.85%29.aspx
  //StdRegProv is preinstalled in the WMI namespaces root\default and root\cimv2.
  //Windows Server 2003, Windows XP, Windows 2000, Windows NT 4.0, and Windows Me/98/95:  StdRegProv is available only in root\default namespace.
  FWMIService   := FSWbemLocator.ConnectServer(Server, 'root\default', User, Pass);
  //For Windows Vista or Windows 7 you must use the  root\CIMV2 namespace
  //FWMIService   := FSWbemLocator.ConnectServer(Server, 'root\CIMV2', User, Pass);
  FWbemObjectSet:= FWMIService.Get('StdRegProv');
  FInParams     := FWbemObjectSet.Methods_.Item('CheckAccess').InParameters.SpawnInstance_();
  FInParams.hDefKey:=HKEY_LOCAL_MACHINE;
  FInParams.sSubKeyName:='SYSTEM\CurrentControlSet';
  FInParams.uRequired:=KEY_QUERY_VALUE;

  FOutParams    := FWMIService.ExecMethod('StdRegProv', 'CheckAccess', FInParams);
  Writeln(Format('bGranted              %s',[FOutParams.bGranted]));
  Writeln(Format('ReturnValue           %s',[FOutParams.ReturnValue]));
end;

reading a string value

读取字符串值

// The GetStringValue method returns the data value for a named value whose data 
// type is REG_SZ. 
procedure  Invoke_StdRegProv_GetStringValue;
const
  Server = '192.168.52.128';
  User   = 'Administrator';
  Pass   = 'password';
var
  FSWbemLocator   : OLEVariant;
  FWMIService     : OLEVariant;
  FWbemObjectSet  : OLEVariant;
  FInParams       : OLEVariant;
  FOutParams      : OLEVariant;
begin
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  //http://msdn.microsoft.com/en-us/library/aa393664%28v=vs.85%29.aspx
  //StdRegProv is preinstalled in the WMI namespaces root\default and root\cimv2.
  //Windows Server 2003, Windows XP, Windows 2000, Windows NT 4.0, and Windows Me/98/95:  StdRegProv is available only in root\default namespace.
  FWMIService   := FSWbemLocator.ConnectServer(Server, 'root\default', User, Pass);
  //For Windows Vista or Windows 7 you must use the  root\CIMV2 namespace
  //FWMIService   := FSWbemLocator.ConnectServer(Server, 'root\CIMV2', User, Pass);
  FWbemObjectSet:= FWMIService.Get('StdRegProv');
  FInParams     := FWbemObjectSet.Methods_.Item('GetStringValue').InParameters.SpawnInstance_();
  FInParams.hDefKey:=HKEY_LOCAL_MACHINE;
  FInParams.sSubKeyName:='SOFTWARE\Borland\Delphi.0';
  FInParams.sValueName:='App';

  FOutParams    := FWMIService.ExecMethod('StdRegProv', 'GetStringValue', FInParams);
  Writeln(Format('sValue                %s',[FOutParams.sValue]));
  Writeln(Format('ReturnValue           %s',[FOutParams.ReturnValue]));
end;

writing a string value

写入字符串值

// The SetStringValue method sets the data value for a named value whose data type 
// is REG_SZ.
procedure  Invoke_StdRegProv_SetStringValue;
const
  Server = '192.168.52.128';
  User   = 'Administrator';
  Pass   = 'password';
var
  FSWbemLocator   : OLEVariant;
  FWMIService     : OLEVariant;
  FWbemObjectSet  : OLEVariant;
  FInParams       : OLEVariant;
  FOutParams      : OLEVariant;
begin
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer(Server, 'root\default', User, Pass);
  FWbemObjectSet:= FWMIService.Get('StdRegProv');
  FInParams     := FWbemObjectSet.Methods_.Item('SetStringValue').InParameters.SpawnInstance_();
  FInParams.hDefKey:=HKEY_LOCAL_MACHINE;
  FInParams.sSubKeyName:='SOFTWARE\Borland\Delphi.0';
  FInParams.sValueName:='Dummy';
  FInParams.sValue:='ADummyValue';

  FOutParams    := FWMIService.ExecMethod('StdRegProv', 'SetStringValue', FInParams);
  Writeln(Format('ReturnValue           %s',[FOutParams.ReturnValue]));
end;

For more options you must check the documentationabout this class.

有关更多选项,您必须查看有关此类的文档

I hope this help you.

我希望这对你有帮助。

回答by RRUZ

Now using the windows API , the TRegistry.RegistryConnectfunction internally call the RegConnectRegistrywindows function , the documentation says :

现在使用windows API,TRegistry.RegistryConnect函数内部调用RegConnectRegistrywindows函数,文档说:

If the current user does not have proper access to the remote computer, the call to RegConnectRegistry fails. To connect to a remote registry, call LogonUser with LOGON32_LOGON_NEW_CREDENTIALS and ImpersonateLoggedOnUser before calling RegConnectRegistry.

Windows 2000:  One possible workaround is to establish a session

to an administrative share such as IPC$ using a different set of credentials. To specify credentials other than those of the current user, use the WNetAddConnection2 function to connect to the share. When you have finished accessing the registry, cancel the connection.

Windows XP Home Edition:  You cannot use this function to connect to

a remote computer running Windows XP Home Edition. This function does work with the name of the local computer even if it is running Windows XP Home Edition because this bypasses the authentication layer.

如果当前用户没有对远程计算机的正确访问权限,则对 RegConnectRegistry 的调用将失败。要连接到远程注册表,请在调用 RegConnectRegistry 之前使用 LOGON32_LOGON_NEW_CREDENTIALS 和 ImpersonateLoggedOnUser 调用 LogonUser。

Windows 2000:  One possible workaround is to establish a session

到管理共享,例如使用不同的凭据集的 IPC$。要指定当前用户以外的凭据,请使用 WNetAddConnection2 函数连接到共享。完成访问注册表后,取消连接。

Windows XP Home Edition:  You cannot use this function to connect to

一台运行 Windows XP Home Edition 的远程计算机。即使本地计算机运行的是 Windows XP Home Edition,该函数也可以使用本地计算机的名称,因为这会绕过身份验证层。

check the next samples

检查下一个样品

using the windows api registry functions

使用 windows api 注册表功能

uses
  Windows,
  Registry,
  SysUtils;

procedure AccessRemoteRegistry(const lpMachineName, lpszUsername , lpszPassword: PChar);
const
 LOGON32_LOGON_NEW_CREDENTIALS = 9;
 REG_OPTION_OPEN_LINK          = 
AccessRemoteRegistry('\192.168.52.128','NormalUser','password');
000008; var netResource : TNetResource; dwFlags : DWORD; dwRetVal : DWORD; phToken : THandle; phkResult : HKEY; phkResult2 : HKEY; lpType : DWORD; lpData : PByte; lpcbData : DWORD; begin ZeroMemory(@netResource, SizeOf(netResource)); netResource.dwType := RESOURCETYPE_ANY; netResource.lpLocalName := nil; netResource.lpRemoteName:= lpMachineName; netResource.lpProvider := nil; dwFlags := CONNECT_UPDATE_PROFILE; dwRetVal := WNetAddConnection2(netResource, lpszPassword, lpszUsername, dwFlags); if dwRetVal=NO_ERROR then begin try Writeln('Connected'); if LogonUser(lpszUsername, nil, lpszPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50,phToken) then begin try if ImpersonateLoggedOnUser(phToken) then begin try dwRetVal:=RegConnectRegistry(lpMachineName,HKEY_LOCAL_MACHINE,phkResult); if dwRetVal = ERROR_SUCCESS then begin dwRetVal:=RegOpenKeyEx(phkResult,PChar('SOFTWARE\Borland\Delphi.0'), REG_OPTION_OPEN_LINK, KEY_QUERY_VALUE, phkResult2); if dwRetVal = ERROR_SUCCESS then begin try lpType:=REG_SZ; //get the size of the buffer dwRetVal:=RegQueryValueEx(phkResult2, PChar('App'), nil, @lpType, nil, @lpcbData); if dwRetVal = ERROR_SUCCESS then begin GetMem(lpData,lpcbData); try dwRetVal:=RegQueryValueEx(phkResult2, 'App', nil, @lpType, lpData, @lpcbData); if dwRetVal = ERROR_SUCCESS then WriteLn(PChar(lpData)) else Writeln(Format('RegQueryValueEx error %d',[dwRetVal])); finally FreeMem(lpData); end; end else Writeln(Format('RegQueryValueEx error %d',[dwRetVal])); finally RegCloseKey(phkResult2); end; end else Writeln(Format('RegOpenKeyEx error %d',[dwRetVal])); end else Writeln(Format('RegConnectRegistry error %d',[dwRetVal])); finally RevertToSelf; end; end else RaiseLastOSError; finally CloseHandle(phToken); end; end else RaiseLastOSError; finally dwRetVal:=WNetCancelConnection2(netResource.lpRemoteName, CONNECT_UPDATE_PROFILE, false); if dwRetVal<>NO_ERROR then Writeln(Format('WNetCancelConnection2 error %d',[dwRetVal])); end; end else Writeln(Format('WNetAddConnection2 Connection error %d',[dwRetVal])); end;

use in this way

以这种方式使用

procedure AccessRemoteRegistry2(const lpMachineName, lpszUsername , lpszPassword: PChar);
const
 LOGON32_LOGON_NEW_CREDENTIALS = 9;
 REG_OPTION_OPEN_LINK          = 
AccessRemoteRegistry2('\192.168.52.128','NormalUser','password');
000008; var netResource : TNetResource; dwFlags : DWORD; dwRetVal : DWORD; phToken : THandle; Reg : TRegistry; begin ZeroMemory(@netResource, SizeOf(netResource)); netResource.dwType := RESOURCETYPE_ANY; netResource.lpLocalName := nil; netResource.lpRemoteName:= lpMachineName; netResource.lpProvider := nil; dwFlags := CONNECT_UPDATE_PROFILE; dwRetVal := WNetAddConnection2(netResource, lpszPassword, lpszUsername, dwFlags); if dwRetVal=NO_ERROR then begin try Writeln('Connected'); if LogonUser(lpszUsername, nil, lpszPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50,phToken) then begin try if ImpersonateLoggedOnUser(phToken) then begin try reg:=TRegistry.Create; try reg.RootKey:=HKEY_LOCAL_MACHINE; if reg.RegistryConnect(lpMachineName) then if reg.OpenKey('SOFTWARE\Borland\Delphi.0',False) then WriteLn(reg.ReadString('App')); finally reg.CloseKey; reg.Free; end; finally RevertToSelf; end; end else RaiseLastOSError; finally CloseHandle(phToken); end; end else RaiseLastOSError; finally dwRetVal:=WNetCancelConnection2(netResource.lpRemoteName, CONNECT_UPDATE_PROFILE, false); if dwRetVal<>NO_ERROR then Writeln(Format('WNetCancelConnection2 error %d',[dwRetVal])); end; end else Writeln(Format('WNetAddConnection2 Connection error %d',[dwRetVal])); end;

using the TRegistry delphi class

使用 TRegistry delphi 类

public class IPCShareSession : IDisposable
{
    #region PInvoke Functions,Structs,Enums

    [DllImport("mpr.dll", CharSet = CharSet.Unicode)]
    private static extern int WNetAddConnection2(IntPtr NetResource, string Password, string Username, WNetConnect Flags);

    [DllImport("mpr.dll", CharSet = CharSet.Unicode)]
    private static extern int WNetCancelConnection2(string ServerName, WNetDisconnect Flags, bool Force);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private class NETRESOURCE
    {
        public WNetResourceScope dwScope = 0;
        public WNetResourceType dwType = 0;
        public WNetResourceDisplayType dwDisplayType = 0;
        public WNetResourceUsage dwUsage = 0;
        public string lpLocalName = null;
        public string lpRemoteName = null;
        public string lpComment = null;
        public string lpProvider = null;
    };

    private enum WNetResourceScope
    {
        Connected = 0x1,
        GlobalNetwork = 0x2,
        Remembered = 0x3,
        Recent = 0x4,
        Context = 0x5
    }

    public enum WNetResourceType
    {
        Any = 0,
        Disk = 1,
        Print = 2,
        Reserved = 8
    }

    private enum WNetResourceUsage
    {
        Connectable = 0x1,
        Container = 0x2,
        NoLocalDevice = 0x4,
        Sibling = 0x8,
        Attached = 0x10,
        All = Connectable | Container | Attached
    }

    private enum WNetResourceDisplayType
    {
        Generic = 0x0,
        Domain = 0x1,
        Server = 0x2,
        Share = 0x3,
        File = 0x4,
        Group = 0x5,
        Network = 0x6,
        Root = 0x7,
        ShareAdmin = 0x8,
        Directory = 0x9,
        Tree = 0xa,
        NDSContainer = 0xb
    }

    private enum WNetConnect : int
    {
        CONNECT_UPDATE_PROFILE = 0x00000001,
        CONNECT_UPDATE_RECENT = 0x00000002,
        CONNECT_TEMPORARY = 0x00000004,
        CONNECT_INTERACTIVE = 0x00000008,
        CONNECT_PROMPT = 0x00000010,
        CONNECT_REDIRECT = 0x00000080,
        CONNECT_CURRENT_MEDIA = 0x00000200,
        CONNECT_COMMANDLINE = 0x00000800,
        CONNECT_CMD_SAVECRED = 0x00001000,
        CONNECT_CRED_RESET = 0x00002000
    }

    private enum WNetDisconnect
    {
        DISCONNECT=0,
        DISCONNECT_AND_REMOVE=1
    }

    #endregion

    private string ServerShare = null;
    private NETRESOURCE serverNR = null;
    private bool bConnected = false;

    public IPCShareSession(string ServerName, string ShareName = "IPC$")
    {
        ServerShare = string.Format(@"\{0}\{1}", ServerName, ShareName);
        //Setup nr to be a IPC share session
        serverNR = new NETRESOURCE
        {
            dwType = WNetResourceType.Disk,
            lpLocalName = null,
            lpRemoteName = ServerShare,
            lpProvider = null,
            dwDisplayType = WNetResourceDisplayType.ShareAdmin,
            dwUsage = WNetResourceUsage.Connectable,
        };
    }

    public int Connect()
    {
        if(!bConnected)
        {
            IntPtr pNR = Marshal.AllocHGlobal(Marshal.SizeOf(serverNR));
            Marshal.StructureToPtr(serverNR, pNR, false);
            int ret = WNetAddConnection2(pNR, null, null, WNetConnect.CONNECT_TEMPORARY);
            //Free our unmanaged pointer now that we're done with it.
            Marshal.FreeHGlobal(pNR);
            bConnected = ret == 0;
            return ret;
        }
        else
        {
            return -1;
        }
    }

    public int Disconnect()
    {
        int ret = 0;
        if (bConnected)
        {
            ret = WNetCancelConnection2(ServerShare, WNetDisconnect.DISCONNECT, true);
            bConnected = false;
        }
        else
        {
            ret = -1;
        }
        return ret;
    }

    #region IDisposable Support

    private bool disposedValue = false; // To detect redundant calls

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                if (bConnected)
                {
                    Disconnect();
                }
            }
            disposedValue = true;
        }
    }

    public void Dispose()
    {
        Dispose(true);
    }

    #endregion


}

use in this way

以这种方式使用

##代码##

回答by C Sharp Conner

In case anyone wants to use WNetAddConnection2 correctly in C#, here's some code. The problem is WNetAddConnection2 expects an IntPtr which holds the buffer of the NETRESOURCE, otherwise you get return code of 487 (0x1E7) which is ERROR_INVALID_ADDRESS. Attempt to access invalid address.

如果有人想在 C# 中正确使用 WNetAddConnection2,这里有一些代码。问题是 WNetAddConnection2 需要一个 IntPtr 来保存 NETRESOURCE 的缓冲区,否则您会得到 487 (0x1E7) 的返回代码,即 ERROR_INVALID_ADDRESS。尝试访问无效地址。

I personally was trying to get registry key that honored elevated privileges and thought by connecting to IPC$ it would work, but of course not, seems only way is using RegCreateKeyEx with the base key from RegConnectRegistry... which is acceptable as long as it actually works once I get around to it, haha!

我个人试图获得具有提升权限的注册表项,并认为通过连接到 IPC$ 会起作用,但当然不是,似乎唯一的方法是将 RegCreateKeyEx 与来自 RegConnectRegistry 的基本项一起使用...只要它是可以接受的一旦我开始使用它实际上就可以工作,哈哈!

##代码##

回答by Warren P

Maybe you need WNetAddConnection3? I don't think it enables remote registry though.

也许你需要WNetAddConnection3?不过,我认为它不会启用远程注册表。

Personally though I would look into WMI, and network-named-pipes, which are methods of remote registry access that Windows supports out of the box.

就我个人而言,我会研究 WMI 和网络命名管道,它们是 Windows 开箱即用支持的远程注册表访问方法。

Also, you know about TRegistry.RegistryConnect. It doesn't seem to have the userid/password, thing you want, but I am still not clear why that isn't enough.

此外,您还了解 TRegistry.RegistryConnect。它似乎没有用户名/密码,你想要的东西,但我仍然不清楚为什么这还不够。