代码之家  ›  专栏  ›  技术社区  ›  Daniel Schilling Aaron Palmer

将.NET SecureString传递给COM互操作

  •  2
  • Daniel Schilling Aaron Palmer  · 技术社区  · 7 年前

    如何手动将BSTR从.NET封送到COM?

    我想把一个秘密藏在 SecureString 从我的.NET应用程序到COM对象上的方法。我想避免转换 安全字符串 String 安全字符串 . 然后,这个秘密就会以明文形式存在于托管内存中,并在那里停留很长一段时间。相反,我正在尝试用正确的方法:将其转换为非托管内存中的BSTR,将其传递给COM方法,然后用 Marshal.ZeroFreeBSTR

    .NET接口TblImp.exe为此COM对象生成,并期望将机密作为 字符串 ,然后它将其编组到BSTR。那不是我想要的,因为这意味着我必须把我的秘密藏在 ,所以接下来 Lean Method for Invoking COM ,我已经创建了自己的界面,以便可以自定义它并摆脱 字符串 . 我从这里开始:

    [ComImport, Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface ITheComObjectDispatcher
    {                                                     // +--- the naughty string
        [return: MarshalAs(UnmanagedType.BStr)]           // |
        [DispId(1)]                                       // V
        string TheMethod([In, MarshalAs(UnmanagedType.BStr)] string secret);
    }
    
    [ComImport, Guid("YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY")]
    public class TheComObject
    {}
    

    这是可行的-但我必须转换 安全字符串 字符串 使用它,就像这样:

    private string UseTheMethod(SecureString secret)
    {
        var comObject = new TheComObject();
        var comObjectDispatcher = (ITheComObjectDispatcher)comObject;
        var secretAsString = NaughtilyConvertSecureStringToString(secret);
        return comObjectDispatcher.TheMethod(secretAsString);
    }
    
    private static string NaughtilyConvertSecureStringToString(SecureString value)
    {
        var valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value);
        try
        {
            return Marshal.PtrToStringUni(valuePtr); // now the secret is in managed memory :(
        }
        finally
        {
            Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
        }
    }
    

    相反,我想使用一种方法 Marshal.SecureStringToBSTR 字符串

        [return: MarshalAs(UnmanagedType.BStr)]
        [DispId(1)]
        string TheMethod([In, MarshalAs(UnmanagedType.BStr)] IntPtr secret);
    

    ... 而这个。。。

        [return: MarshalAs(UnmanagedType.BStr)]
        [DispId(1)]
        string TheMethod([In] IntPtr secret);
    

    ... 用这个呼叫码。。。

    private string UseTheMethod(SecureString secret)
    {
        var comObject = new TheComObject();
        var comObjectDispatcher = (ITheComObjectDispatcher)comObject;
        var secretPtr = Marshal.SecureStringToBSTR(secret);
        try
        {
            return comObjectDispatcher.TheMethod(secretPtr);
        }
        finally
        {
            Marshal.ZeroFreeBSTR(secretPtr);
        }
    }
    

    ... 但它不起作用:错误的值被传递到COM方法。它不会因为错误而失败-它只会给出不同的结果。

    1. 我能像这样直接把BSTR作为IntPtr传递吗?我做错什么了?
    2. 是否有方法调试封送处理过程以查看传递的值?
    1 回复  |  直到 7 年前
        1
  •  3
  •   Simon Mourier    7 年前

    让我们假设您在.idl:

    interface ITheComObjectDispatcher : IDispatch
    {
        HRESULT TheMethod(BSTR secret, [out, retval] BSTR *pOut);
    };
    

    在.NET的tlbimp中会变成这样(如您所见):

    [ComImport, TypeLibType((short) 0x10c0), Guid("D4089F1D-5D83-4D1C-92CD-5941B35D43AA")]
    public interface ITheComObjectDispatcher
    {
        [return: MarshalAs(UnmanagedType.BStr)]
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime), DispId(0x60020000)]
        string TheMethod([MarshalAs(UnmanagedType.BStr)] string secret);
    }
    

    string 论据。 解决方案是在C#中重新定义COM接口,例如(注意 InterfaceIsDual

    [InterfaceType(ComInterfaceType.InterfaceIsDual), Guid("D4089F1D-5D83-4D1C-92CD-5941B35D43AA")]
    public interface ITheComObjectDispatcher2
    {
        [DispId(0x60020000)]
        IntPtr TheMethod(IntPtr secret);
    }
    

    像这样使用:

    var comObject = new TheComObject();
    var comObjectDispatcher = (ITheComObjectDispatcher2)comObject;
    var ptr = Marshal.SecureStringToBSTR(mySecureString);
    try
    {
        var outPtr = doc.TheMethod(ptr);
    
        // get the output string
        var output = Marshal.PtrToStringBSTR(outPtr);
    
        // free the callee-allocated BSTR
        Marshal.FreeBSTR(outPtr);
    }
    finally
    {
        // free the secure string allocated BSTR
        Marshal.ZeroFreeBSTR(ptr);
    }
    
    推荐文章