我正在尝试从托管代码中调用 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
是 void *
,我发现一些文档表明它可以表示为int
,而b / c可以表示为 null
,我将其设置为 0
(https://docs.microsoft.com/en-us/dotnet/framework/interop/default-marshaling-behavior)。
下面是一个完整且可运行的示例(将崩溃)
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);
}
}
}