【问题标题】:EasyHook, .NET Remoting sharing interface between both client and server?EasyHook,.NET Remoting 客户端和服务器之间的共享接口?
【发布时间】:2012-11-01 12:51:46
【问题描述】:

IPC客户端和IPC服务器如何调用共享远程接口(继承MarshalByRefObject的类)进行通信,而不必将接口类放在注入应用程序中?例如,如果我将接口类放在注入目标进程的 injected 库项目中,我的注入应用程序将无法引用该接口。

编辑:我已经回答了下面的问题。

【问题讨论】:

    标签: c# c#-4.0 remoting .net-remoting easyhook


    【解决方案1】:

    截至EasyHook Commit 66751(与 EasyHook 2.7 alpha 相关联),似乎无法在两个客户端(启动您的 DLL 注入的进程中获取远程接口的实例) 和服务器(运行注入的 DLL 的 injected 进程)。

    什么意思?

    好吧,在FileMonProcessMonitor 示例中,请注意共享远程处理接口(FileMonInterface,嵌入在Program.cs,对于Filemon,和DemoInterface,在其自己的文件中, 对于 ProcessMonitor) 被放置在 injecting 程序集中。 FileMonInterface 在 FileMon 项目中。 DemoInterface 在 ProcessMonitor 项目中。

    为什么不是另一轮?为什么不把FileMonInterface放在项目FileMonInject中,把DemoInterface放在ProcMonInject中呢? 因为调用应用程序(FileMon 和 ProcessMonitor)将无法再访问这些接口。

    原因是因为 EasyHook 内部使用:

    RemotingConfiguration.RegisterWellKnownServiceType(
                    typeof(TRemoteObject),
                    ChannelName,
                    InObjectMode);
    

    此远程调用允许客户端调用您的(服务器)接口,但服务器本身(您,应用程序)无法调用它。

    解决方案

    改为:

    // Get the instance by simply calling `new RemotingInterface()` beforehand somewhere
    RemotingServices.Marshal(instanceOfYourRemotingInterfaceHere, ChannelName);
    

    我所做的是向 EasyHook 的 RemoteHook.IpcCreateServer() 添加一个重载,以接受我进行 .NET 远程处理的新“方式”。

    它很丑,但它有效:

    代码

    用以下代码替换整个 IpcCreateServer 方法(从大括号到大括号)。这里显示了两种方法。一是更详细的过载。第二个是调用我们重载的“原始”方法。

    public static IpcServerChannel IpcCreateServer<TRemoteObject>(
            ref String RefChannelName,
            WellKnownObjectMode InObjectMode,
            TRemoteObject ipcInterface, String ipcUri, bool useNewMethod,
            params WellKnownSidType[] InAllowedClientSIDs) where TRemoteObject : MarshalByRefObject
        {
            String ChannelName = RefChannelName ?? GenerateName();
    
            ///////////////////////////////////////////////////////////////////
            // create security descriptor for IpcChannel...
            System.Collections.IDictionary Properties = new System.Collections.Hashtable();
    
            Properties["name"] = ChannelName;
            Properties["portName"] = ChannelName;
    
            DiscretionaryAcl DACL = new DiscretionaryAcl(false, false, 1);
    
            if (InAllowedClientSIDs.Length == 0)
            {
                if (RefChannelName != null)
                    throw new System.Security.HostProtectionException("If no random channel name is being used, you shall specify all allowed SIDs.");
    
                // allow access from all users... Channel is protected by random path name!
                DACL.AddAccess(
                    AccessControlType.Allow,
                    new SecurityIdentifier(
                        WellKnownSidType.WorldSid,
                        null),
                    -1,
                    InheritanceFlags.None,
                    PropagationFlags.None);
            }
            else
            {
                for (int i = 0; i < InAllowedClientSIDs.Length; i++)
                {
                    DACL.AddAccess(
                        AccessControlType.Allow,
                        new SecurityIdentifier(
                            InAllowedClientSIDs[i],
                            null),
                        -1,
                        InheritanceFlags.None,
                        PropagationFlags.None);
                }
            }
    
            CommonSecurityDescriptor SecDescr = new CommonSecurityDescriptor(false, false,
                ControlFlags.GroupDefaulted |
                ControlFlags.OwnerDefaulted |
                ControlFlags.DiscretionaryAclPresent,
                null, null, null,
                DACL);
    
            //////////////////////////////////////////////////////////
            // create IpcChannel...
            BinaryServerFormatterSinkProvider BinaryProv = new BinaryServerFormatterSinkProvider();
            BinaryProv.TypeFilterLevel = TypeFilterLevel.Full;
    
            IpcServerChannel Result = new IpcServerChannel(Properties, BinaryProv, SecDescr);
    
            if (!useNewMethod)
            {
                ChannelServices.RegisterChannel(Result, false);
    
                RemotingConfiguration.RegisterWellKnownServiceType(
                    typeof(TRemoteObject),
                    ChannelName,
                    InObjectMode);
            }
            else
            {
                ChannelServices.RegisterChannel(Result, false);
    
                ObjRef refGreeter = RemotingServices.Marshal(ipcInterface, ipcUri);
            }
    
            RefChannelName = ChannelName;
    
            return Result;
        }
    
        /// <summary>
        /// Creates a globally reachable, managed IPC-Port.
        /// </summary>
        /// <remarks>
        /// Because it is something tricky to get a port working for any constellation of
        /// target processes, I decided to write a proper wrapper method. Just keep the returned
        /// <see cref="IpcChannel"/> alive, by adding it to a global list or static variable,
        /// as long as you want to have the IPC port open.
        /// </remarks>
        /// <typeparam name="TRemoteObject">
        /// A class derived from <see cref="MarshalByRefObject"/> which provides the
        /// method implementations this server should expose.
        /// </typeparam>
        /// <param name="InObjectMode">
        /// <see cref="WellKnownObjectMode.SingleCall"/> if you want to handle each call in an new
        /// object instance, <see cref="WellKnownObjectMode.Singleton"/> otherwise. The latter will implicitly
        /// allow you to use "static" remote variables.
        /// </param>
        /// <param name="RefChannelName">
        /// Either <c>null</c> to let the method generate a random channel name to be passed to 
        /// <see cref="IpcConnectClient{TRemoteObject}"/> or a predefined one. If you pass a value unequal to 
        /// <c>null</c>, you shall also specify all SIDs that are allowed to connect to your channel!
        /// </param>
        /// <param name="InAllowedClientSIDs">
        /// If no SID is specified, all authenticated users will be allowed to access the server
        /// channel by default. You must specify an SID if <paramref name="RefChannelName"/> is unequal to <c>null</c>.
        /// </param>
        /// <returns>
        /// An <see cref="IpcChannel"/> that shall be keept alive until the server is not needed anymore.
        /// </returns>
        /// <exception cref="System.Security.HostProtectionException">
        /// If a predefined channel name is being used, you are required to specify a list of well known SIDs
        /// which are allowed to access the newly created server.
        /// </exception>
        /// <exception cref="RemotingException">
        /// The given channel name is already in use.
        /// </exception>
        public static IpcServerChannel IpcCreateServer<TRemoteObject>(
            ref String RefChannelName,
            WellKnownObjectMode InObjectMode,
            params WellKnownSidType[] InAllowedClientSIDs) where TRemoteObject : MarshalByRefObject
        {
            return IpcCreateServer<TRemoteObject>(ref RefChannelName, InObjectMode, null, null, false, InAllowedClientSIDs);
        }
    

    就是这样。这就是你需要改变的一切。您不必更改 IpcCreateClient()。

    使用代码

    以下是您将如何使用新的重载方法:

    说你有

    public class IpcInterface : MarshalByRefObject { /* ... */ }
    

    作为您的共享远程接口。

    创建它的一个新实例,并存储它的引用。您将使用它与您的客户沟通。

    var myIpcInterface = new IpcInterface(); // Keep this reference to communicate!
    

    之前创建远程通道的方法如下:

            ipcServer = RemoteHooking.IpcCreateServer<IpcInterface>(ref IpcChannelName, WellKnownObjectMode.Singleton, WellKnownSidType.WorldSid);
    

    现在创建远程通道的方法如下:

            ipcServer = RemoteHooking.IpcCreateServer<IpcInterface>(ref IpcChannelName, WellKnownObjectMode.Singleton, myIpcInterface, IpcChannelName, true, WellKnownSidType.WorldSid);
    

    别忘了...

    我从this StackOverflow post 得到了这个解决方案。请务必按他说的做,重写 InitializeLifetimeService 以返回 null:

    public override object InitializeLifetimeService()
    {
        // Live "forever"
        return null;
    }
    

    我认为这应该是为了防止客户端丢失远程接口。

    用途

    现在,您不必将远程接口文件与注入项目放在同一目录中,而是可以专门为您的接口文件创建一个库。

    这个解决方案对于那些有过 .NET 远程处理经验的人来说可能是常识,但我对此一无所知(可能在这篇文章中使用了错误的接口一词)。

    【讨论】:

      猜你喜欢
      • 2017-06-14
      • 2013-02-17
      • 1970-01-01
      • 2021-03-10
      • 1970-01-01
      • 2016-12-04
      • 2015-08-07
      • 1970-01-01
      相关资源
      最近更新 更多