从P / Invoked代码中获取错误的IntPtr

时间:2014-10-02 12:43:33

标签: c# pinvoke intptr

首先,如果我的标题在技术上不准确,我会道歉。我不确定究竟发生了什么,并且无论如何都能描述它。

我正在尝试为程序中的特定用途解码SSL证书。我p /调用了所有必要的CryptoAPI函数和结构,并且在调试模式下,一切都按预期工作。但是,当程序作为服务在发布模式下运行时(这是该程序的目的),我在尝试解码扩展时遇到访问冲突。为了使扩展更容易解码,我创建了一个可用于表示任何扩展的泛型类。此类包含TStruct类型的对象,该对象表示扩展所基于的基础结构。它有一个byte []类型的EncryptedValue字段,它将加密/解密扩展数据并将其放在结构中。基类如下:

public abstract class ExtensionBase<TStruct> : IExtensionBase where TStruct : new()
{
    //The underlying struct
    protected TStruct objectData = new TStruct();

    //The identifier of the struct, ie: szOID_BASIC_CONSTRAINTS
    public string Identifier { get; protected set; }
    //An enum representing the structure type, ie: X509_BASIC_CONSTRAINTS
    public CertStructType StructureType { get; protected set; }
    //Determines if the extension is critical
    public bool IsCritical { get; protected set; }

    //Overridden in any child class to determine if that extension actually contains
    //data that should be encoded
    public abstract bool HasData { get; }

    //Encrypts/decrypts the data from/to the underlying structure
    public virtual byte[] EncryptedValue
    {
        get
        {
            uint encodedSize = 0;

            //PinnedHandle is a class that I wrote to wrap a GCHandle.
            //It has an implicit cast to IntPtr that returns GCHandle.AddrOfPinnedObject
            //The finalizer of the class releases the GCHandle if it is a valid handle
            IntPtr dataPtr = new PinnedHandle(objectData);

            byte[] retVal = null;

            if (StructureType != CertStructType.None)
            {
                if (!Crypt32.CryptEncodeObjectEx((uint)CertEncoding.X509Asn, 
                                                 (uint)StructureType, 
                                                 dataPtr, 
                                                 0,
                                                 IntPtr.Zero, 
                                                 null, 
                                                 ref encodedSize))
                    throw new Win32Exception();

                retVal = new byte[encodedSize];

                if (!Crypt32.CryptEncodeObjectEx((uint)CertEncoding.X509Asn, 
                                                 (uint)StructureType, 
                                                 dataPtr, 
                                                 0, 
                                                 IntPtr.Zero, 
                                                 retVal, 
                                                 ref encodedSize))
                    throw new Win32Exception();
            }
            else
            {
                if (!Crypt32.CryptEncodeObjectEx((uint)CertEncoding.X509Asn, 
                                                 Identifier, 
                                                 dataPtr, 
                                                 0, 
                                                 IntPtr.Zero, 
                                                 null, 
                                                 ref encodedSize))
                    throw new Win32Exception();

                retVal = new byte[encodedSize];

                if (!Crypt32.CryptEncodeObjectEx((uint)CertEncoding.X509Asn, 
                                                  Identifier, 
                                                  dataPtr, 
                                                  0, 
                                                  IntPtr.Zero, 
                                                  retVal, 
                                                  ref encodedSize))
                    throw new Win32Exception();
            }

            return retVal;
        }
        set
        {
            uint decodedSize = 0;

            IntPtr decodedData = IntPtr.Zero;
            if(StructureType != CertStructType.None)
                decodedData = Crypt32.CryptDecodeObjectEx(StructureType, value);
            else
                decodedData = Crypt32.CryptDecodeObjectEx(Identifier, value);

            TStruct data = (TStruct)Marshal.PtrToStructure(decodedData, typeof(TStruct));

            objectData = data;

            Marshal.FreeHGlobal(decodedData);
        }
    }

    public ExtensionBase(string id)
    {
        Identifier = id;
        StructureType = CertStructType.None;
    }

    public ExtensionBase(string id, CertStructType structType)
    {
        Identifier = id;
        StructureType = structType;
    }
}

给我带来问题的一个子类是CertKeyUsage类,它使用CRYPT_BIT_BLOB结构来表示它的数据:

public class CertKeyUsage : ExtensionBase<CertKeyUsageFlags, CRYPT_BIT_BLOB>
{
    public override bool HasData
    {
        get { return Value != CertKeyUsageFlags.None; }
    }

    public override unsafe byte[] EncryptedValue
    {
        get
        {
            CertKeyUsageFlags flags = Value;
            objectData.cbData = 2;
            objectData.cUnusedBits = 0;
            objectData.pbData = new IntPtr(&flags);

            return base.EncryptedValue;
        }
        set
        {
            try
            {
                //The following code was taken directly from Microsoft's implementation
                //of X509Certificate
                base.EncryptedValue = value;
                if (objectData.cbData > 4)
                    objectData.cbData = 4;

                byte[] keyUsage = new byte[4];

                //This if statement returns true, and the following Marshal.Copy statement
                //is where the program crashes with the Access Violation.  As it is an unmanaged
                //exception, the try/catch block doesn't do anything and the app dies
                if (objectData.pbData != IntPtr.Zero)
                    Marshal.Copy(objectData.pbData, keyUsage, 0, (int) objectData.cbData);
                Value = (CertKeyUsageFlags) BitConverter.ToUInt32(keyUsage, 0);
            }
            catch
            {

            }
        }
    }

    public CertKeyUsage()
        : base(CertOid.szOID_KEY_USAGE, CertStructType.X509KeyUsage)
    {
        IsCritical = true;
    }
}

这段代码与调试版本有什么不同,会导致上述现场的访问冲突?我能做些什么来让它正常工作。这个特定的扩展并不重要,我可以跳过它,但在注释掉导致异常的上面的代码后,另一个扩展将崩溃。我不能评论所有扩展,并且它移动到不同位置的事实告诉我,我的代码存在一些我遗漏的潜在问题。任何帮助或建议将不胜感激。

这些是我正在调用的p /被调用函数:

    [DllImport("crypt32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
    public static extern bool CryptEncodeObjectEx(uint certEncodingType,
                                                  [MarshalAs(UnmanagedType.LPStr)]
                                                  string structType, 
                                                  IntPtr structInfo, 
                                                  uint flags, 
                                                  IntPtr encodePara, 
                                                  byte[] encodedData, 
                                                  [In, Out] ref uint encodedSize);

    [DllImport("crypt32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
    public static extern bool CryptEncodeObjectEx(uint certEncodingType, 
                                                  uint structType, 
                                                  IntPtr structInfo, 
                                                  uint flags, 
                                                  IntPtr encodePara, 
                                                  byte[] encodedData,
                                                  [In, Out] ref uint encodedSize);

    [DllImport("crypt32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
    public static extern bool CryptDecodeObjectEx(uint certEncodingType, 
                                                  [MarshalAs(UnmanagedType.LPStr)]
                                                  string structType, 
                                                  byte[] encodedData, 
                                                  uint encodedDataSize,
                                                  uint flags,
                                                  IntPtr encodePara, 
                                                  IntPtr decodedData, 
                                                  [In, Out] ref uint decodedDataSize);

    [DllImport("crypt32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
    public static extern bool CryptDecodeObjectEx(uint certEncodingType, 
                                                  uint structType, 
                                                  byte[] encodedData, 
                                                  uint encodedDataSize, 
                                                  uint flags, 
                                                  IntPtr encodePara, 
                                                  IntPtr decodedData,
                                                  [In, Out] ref uint decodedDataSize);

包装CryptDecodeObjectEx的函数:

    public static IntPtr CryptDecodeObjectEx(string structType, byte[] encodedData)
    {
        uint encodedSize = (uint)encodedData.Length;
        uint decodedSize = 0;
        IntPtr decodedData = IntPtr.Zero;

        if (!CryptDecodeObjectEx((uint)CertEncoding.X509Asn,
                                 structType,
                                 encodedData,
                                 encodedSize, 
                                 0, 
                                 IntPtr.Zero, 
                                 decodedData, 
                                 ref decodedSize))
            throw new Win32Exception();

        decodedData = Marshal.AllocHGlobal((int)decodedSize);

        if (!CryptDecodeObjectEx((uint)CertEncoding.X509Asn, 
                                  structType, 
                                  encodedData, 
                                  encodedSize, 
                                  0, 
                                  IntPtr.Zero, 
                                  decodedData, 
                                  ref decodedSize))
            throw new Win32Exception();

        return decodedData;
    }

    public static IntPtr CryptDecodeObjectEx(uint structType, byte[] encodedData)
    {
        uint encodedSize = (uint)encodedData.Length;
        uint decodedSize = 0;
        IntPtr decodedData = IntPtr.Zero;

        if (!CryptDecodeObjectEx((uint)CertEncoding.X509Asn, 
                                  structType, 
                                  encodedData, 
                                  encodedSize, 
                                  0, 
                                  IntPtr.Zero, 
                                  decodedData, 
                                  ref decodedSize))
            throw new Win32Exception();

        decodedData = Marshal.AllocHGlobal((int)decodedSize);

        if (!CryptDecodeObjectEx((uint)CertEncoding.X509Asn, 
                                  structType, 
                                  encodedData, 
                                  encodedSize, 
                                  0, 
                                  IntPtr.Zero, 
                                  decodedData, 
                                  ref decodedSize))
            throw new Win32Exception();

        return decodedData;
    }

一时兴起,我决定将程序编译为X86而不是AnyCPU,一切都按预期工作。这让我相信问题实际上出现在这部分代码中:

    public static IntPtr Next<T>(this IntPtr val)
    {
        T retVal = (T)Marshal.PtrToStructure(val, typeof(T));

        if (Environment.Is64BitProcess)
            return (IntPtr)((long)val + Marshal.SizeOf(retVal));

        return (IntPtr)((int)val + Marshal.SizeOf(retVal));
    }

这是我用来通过指向结构数组的指针枚举的代码。传入结构类型,并返回IntPtr到数组中的下一个结构。 32位版本正常工作,但显然我的64位版本的逻辑有点缺乏。

0 个答案:

没有答案