.net - 将 .NET SecureString 传递给 COM Interop

标签 .net com marshalling bstr securestring

如何手动将 BSTR 从 .NET 编码(marshal)至 COM?

我正在尝试将存储在 SecureString 中的 secret 从我的 .NET 应用程序传递到 COM 对象上的方法。我想避免将 SecureString 转换为 String,因为这不是您应该使用 SecureString 的方式。然后,该 secret 将以明文形式存在于托管内存中,并在那里保留很长一段时间。相反,我尝试以正确的方式进行操作:将其转换为非托管内存中的 BSTR,将其传递给 COM 方法,然后使用 Marshal.ZeroFreeBSTR 清除 BSTR,以便密码仅在有限的时间内暴露。

TblImp.exe 为此 COM 对象生成的 .NET 接口(interface)期望将 secret 作为 String 传递,然后将其编码到 BSTR。这不是我想要的,因为这意味着我必须将我的 secret 放入 String 中,因此请遵循此 Lean Method for Invoking COM ,我创建了自己的界面,以便可以自定义它并摆脱 String。这就是我要开始的内容:

[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
{}

这可行 - 但我必须将 SecureString 转换为 String 才能使用它,如下所示:

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 这样的方法并跳过到 String 的转换。我已经尝试过这个...

    [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. 有没有办法调试编码(marshal)处理过程以查看正在传递什么值?

最佳答案

假设您在 .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 接口(interface),例如如下所示(请注意 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);
}

关于.net - 将 .NET SecureString 传递给 COM Interop,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52224452/

相关文章:

c# - StaTaskScheduler和STA线程消息泵送

mfc - 我在 VS-2019 中缺少添加新项目 Add "MFC Class From Typelib"

com - IDA Pro 中是否可以将结构体字段偏移到 .data 段中定义的 vtable?

c# - 下载并实例化 COM 控件

json - 所有字段的 "omitempty"

c# - 在 C# 中编码全局变量

c# - 多步算法的设计模式

c# - 从给定日期计算月份的星期几?

.net - Spring.Net(和NHibernate?),缺少异常翻译器

c# - 从 C# 应用程序使用 C++ 库时出现编码错误