gpt4 book ai didi

c# - 带有非导出函数的 DllImport。为什么有效?

转载 作者:行者123 更新时间:2023-12-03 14:36:57 33 4
gpt4 key购买 nike

我刚刚在 C# 中遇到了 DllImport 的奇怪行为,我无法解释。我想知道它是如何可能的,以及我可以在哪里阅读它。案例是通过 DllImport 可以调用不真正导出表单 dll 的函数。就我而言,它是 kernel32.dll 和函数 ZeroMemory(但具有复制/移动/填充内存这样的行为)。所以,我的代码:

[DllImport("kernel32", EntryPoint = "LoadLibraryW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr LoadLibrary(string libName);

[DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr module, string procName);

//WTF???
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool ZeroMemory(IntPtr address, int size);

static void TestMemory()
{
IntPtr mem = Marshal.AllocHGlobal(100); //Allocate memory block of 100 bytes size
Marshal.WriteByte(mem, 55); //Write some value in the first byte
ZeroMemory(mem, 100); //Clearing block of memory
byte firstByte = Marshal.ReadByte(mem); //Read the first byte of memory block
Console.WriteLine(firstByte); //Output 0 (not 55) - ZeroMemory is working

//Getting address of ZeroMemory from kernel32.dll
IntPtr kernelHandle = LoadLibrary("kernel32.dll");
IntPtr address = GetProcAddress(kernelHandle, "ZeroMemory");
Console.WriteLine(address.ToString("X")); //Output 0 - Library kernel32.dll DOESN'T export function ZeroMemory!!!

//Testing GetProcAddress via getting address of some exported function
Console.WriteLine(GetProcAddress(kernelHandle, "AllocConsole").ToString("X")); //Output some address value - all is OK.
}
没有抛出 EntryPointNotFoundException - 代码工作正常。如果将 ZeroMemory 的名称更改为 ZeroMemory1 或类似的名称 - 将引发异常。但是在kernel32.dll的导出表中我们看到:
There is NO ZeroMemory!!!
根本没有 ZeroMemory 功能!
如果我们查看 msdn ,我们读到 ZeroMemory 只是 C++ 的 WinBase.h 头文件中的一个宏。在里面我们看到:
#define RtlMoveMemory memmove
#define RtlCopyMemory memcpy
#define RtlFillMemory(d,l,f) memset((d), (f), (l))
#define RtlZeroMemory(d,l) RtlFillMemory((d),(l),0)
#define MoveMemory RtlMoveMemory
#define CopyMemory RtlCopyMemory
#define FillMemory RtlFillMemory
#define ZeroMemory RtlZeroMemory
显然,在 C++ ZeroMemory 中,它实际上是通过 ntdll.dll 中的 RtlFillMemory 工作的。但它是在 C++ 中的!!!为什么它在 C# 中工作?关于 DllImport 属性的官方文档 here我们可以阅读下一个:

As a minimum requirement, you must supply the name of the DLLcontaining the entry point.


但在这种情况下,kernel32.dll 无法连接 ZeroMemory 的入口点。到底是怎么回事??请帮忙。

最佳答案

OP 是正确的 kernel32.dll没有导出ZeroMemory导出,但 C# DllImport不知何故成功地神奇地解决了 ZeroMemory引用正确的 RtlZeroMemory .NET 应用程序中针对框架(但不是核心)的导出。
事实证明,框架代码专门检查了少数记录为内联/宏( MoveMemoryCopyMemory FillMemoryZeroMemory )的 Win32 API,并在内部重新路由到正确的导出。虽然没有正式记录,但这在 MS 批准的 comment 中得到了承认。下.NET Runtime问题。

As an FYI, there were a few special cased P/Invoke names in .NET Framework: MoveMemory, CopyMemory, FillMemory, and ZeroMemory. All of these will work when pointing at kernel32 on .NET Framework, but will fail on .NET Core. Please use the EntryPoint property in the DllImport attribute to get the desired behavior. The proper export names can be found using dumpbin /exports kernel32.dll from a Visual Studio command prompt.


以上建议添加显式 EntryPoint使声明在 Framework 和 Core 中都有效,例如:
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
public static extern void ZeroMemory(IntPtr address, IntPtr count);
神奇的名称重映射发生在开源 .NET 代码之外,但可以在 Simon Mourier 提供的反汇编中清楚地看到在评论中。
enter image description here

关于c# - 带有非导出函数的 DllImport。为什么有效?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66596448/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com