c# - 从本机代码调用 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
,我将其设置为0
(https://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);
}
}
}
解决方案
推荐阅读
- javascript - 如何在工具提示 echarts 中显示图标
- python - Django 如何使用数据迁移填充表?
- flutter - 获取 Widget 的高度并将其传递给 expandHeight
- php - laravel 是否需要单独的 Web 服务器才能运行?
- javascript - 没有数据被插入到我的数据库中
- .net-core - Visual Studio Code 在 .NET Core 项目中找不到对 SignInManager 的引用
- c - clock_gettime() 未定义符号 SunOS
- swift - 如何快速按下所选单元格的IndexPath
- laravel - Api 只能从受信任的站点访问,不能通过任何浏览器、邮递员等访问
- scala - 如果数据框列中的值为 NULL,如何使用 if else 语句引发错误?