首页 > 解决方案 > 从本机代码调用 crypt32.dll CryptProtectData 方法

问题描述

我正在尝试从托管代码调用crypt32.dll方法CryptProtectData,但我的委托声明中似乎没有完全正确的编组类型:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate bool CryptProtectDataDelegate(
    IntPtr pDataIn,             // DATA_BLOB*
    //StringBuilder szDataDescr,  // LPCWSTR
    [MarshalAs(UnmanagedType.LPWStr)] string szDataDescr,   // LPCWSTR
    IntPtr pOptionalEntropy,    // DATA_BLOB*
    int pvReserved,             // PVOID 
    IntPtr pPromptStruct,       // CRYPTPROTECT_PROMPTSTRUCT*
    int dwFlags,                // DWORD 
    IntPtr pDataOut             // DATA_BLOB*
);

当被调用时,

bool theResult = cryptProtectData(
    pDataIn,
    null,
    IntPtr.Zero,    // null,
    0,              // null,
    IntPtr.Zero,    // null,
    flag,
    pDataOut);

导致异常

+CryptProtectDataDelegate::Invoke' 使堆栈不平衡。这可能是因为托管 PInvoke 签名与非托管目标签名不匹配。检查 PInvoke 签名的调用约定和参数是否与目标非托管签名匹配。

的原生定义CryptProtectData

DPAPI_IMP BOOL CryptProtectData(
  DATA_BLOB                 *pDataIn,
  LPCWSTR                   szDataDescr,
  DATA_BLOB                 *pOptionalEntropy,
  PVOID                     pvReserved,
  CRYPTPROTECT_PROMPTSTRUCT *pPromptStruct,
  DWORD                     dwFlags,
  DATA_BLOB                 *pDataOut
);

其中 DPAPI_IMP 定义为

#define DPAPI_IMP DECLSPEC_IMPORT

我注意到我应该为委托定义中的参数使用什么合法类型表示?

假设PVOID是 a void *,我发现一些文档表明它可以表示为 a int,而 b/c 可以是null,我将其设置为0https://docs.microsoft.com/en-us/dotnet/framework/interop /默认编组行为)。


下面是一个完整且可运行的示例(会崩溃)

using System;
using System.Runtime.InteropServices;

namespace CallNativeDLLs
{
    static class NativeMethods
    {
        [DllImport("kernel32.dll")]
        public static extern IntPtr LoadLibrary(string dllToLoad);

        [DllImport("kernel32.dll")]
        public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);

        [DllImport("kernel32.dll")]
        public static extern bool FreeLibrary(IntPtr hModule);
    }

    class Program
    {
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate bool CryptProtectDataDelegate(
            IntPtr pDataIn,             // DATA_BLOB*
            //StringBuilder szDataDescr,  // LPCWSTR
            [MarshalAs(UnmanagedType.LPWStr)] string szDataDescr,   // LPCWSTR
            IntPtr pOptionalEntropy,    // DATA_BLOB*
            int pvReserved,             // PVOID 
            IntPtr pPromptStruct,       // CRYPTPROTECT_PROMPTSTRUCT*
            int dwFlags,                // DWORD 
            IntPtr pDataOut             // DATA_BLOB*
        );

        static void Main(string[] args)
        {
            IntPtr pDll = NativeMethods.LoadLibrary(@"c:\windows\system32\crypt32.DLL");

            IntPtr pAddressOfFunctionToCall = NativeMethods.GetProcAddress(pDll, "CryptProtectData");

            var cryptProtectData = (CryptProtectDataDelegate)Marshal.GetDelegateForFunctionPointer(
                pAddressOfFunctionToCall,
                typeof(CryptProtectDataDelegate));

            IntPtr pDataIn = Marshal.StringToHGlobalAnsi("hi");
            int flag = (int)0x4; //CRYPTPROTECT_LOCAL_MACHINE 
            var pDataOut = new IntPtr();

            // EXCEPTION thrown here
            bool theResult = cryptProtectData(
                pDataIn,
                null,
                IntPtr.Zero,    // null,
                0,              // null,
                IntPtr.Zero,    // null,
                flag,
                pDataOut);

            bool result = NativeMethods.FreeLibrary(pDll);

            Console.WriteLine(theResult);
        }
    }
}

更新:使用Amy 的链接,以下运行(虽然我还没有尝试解密字符串,并且 CRYPTPROTECT_LOCAL_MACHINE 很弱)

using System;
using System.Runtime.InteropServices;

namespace CallNativeDLLs
{
    static class NativeMethods
    {
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct DATA_BLOB
        {
            public int cbData;
            public IntPtr pbData;
        }

        [Flags]
        public enum CryptProtectPromptFlags
        {
            CRYPTPROTECT_PROMPT_ON_UNPROTECT = 0x1,
            CRYPTPROTECT_PROMPT_ON_PROTECT = 0x2
        }

        [Flags]
        public enum CryptProtectFlags
        {
            CRYPTPROTECT_UI_FORBIDDEN = 0x1,
            CRYPTPROTECT_LOCAL_MACHINE = 0x4,
            CRYPTPROTECT_CRED_SYNC = 0x8,
            CRYPTPROTECT_AUDIT = 0x10,
            CRYPTPROTECT_NO_RECOVERY = 0x20,
            CRYPTPROTECT_VERIFY_PROTECTION = 0x40
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct CRYPTPROTECT_PROMPTSTRUCT
        {
            public int cbSize;
            public CryptProtectPromptFlags dwPromptFlags;
            public IntPtr hwndApp;
            public String szPrompt;
        }

        [DllImport("Crypt32.dll",
            SetLastError = true,
            CharSet = System.Runtime.InteropServices.CharSet.Auto)]     
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool CryptProtectData(
            ref DATA_BLOB pDataIn,
            String szDataDescr,
            ref DATA_BLOB pOptionalEntropy,
            IntPtr pvReserved,
            ref CRYPTPROTECT_PROMPTSTRUCT pPromptStruct,
            CryptProtectFlags dwFlags,
            ref DATA_BLOB pDataOut
        );
    }

    class Program
    {
        static void Main(string[] args)
        {
            IntPtr pbData = Marshal.StringToHGlobalAnsi("hi");
            var pDataIn = new NativeMethods.DATA_BLOB{cbData=0,pbData=pbData};
            var pOptionalEntropy = new NativeMethods.DATA_BLOB();
            var pPromptStruct = new NativeMethods.CRYPTPROTECT_PROMPTSTRUCT();
            var pDataOut = new NativeMethods.DATA_BLOB();

            bool theResult = NativeMethods.CryptProtectData(
                ref pDataIn,
                null,
                ref pOptionalEntropy, // null,
                IntPtr.Zero,          // null,
                ref pPromptStruct,    // null,
                NativeMethods.CryptProtectFlags.CRYPTPROTECT_LOCAL_MACHINE,
                ref pDataOut);

            Console.WriteLine(theResult);
        }
    }
}

标签: c#pinvokemarshallingcryptoapi

解决方案


推荐阅读