问题描述
我意识到的一些C#端口,允许从内存/流中加载库,而不是使用通过文件系统工作的LoadLibrary API函数。
在用指针和模拟结果搞砸了一点之后...终于有一些工作按预期。我唯一的问题是对DLLMain的调用总是失败(我尝试用Kernel32.dll和User32.dll)。我不明白为什么,我不知道如何调试问题。
I realized a little C# porting of this application that allows to load libraries from memory/stream instead of using LoadLibrary API function that works through filesystem.After messing around a little bit with pointers and mimatching results... finally I have something working as intended. The only problem I have is that the call to DLLMain always fails (I tried it with Kernel32.dll and User32.dll). I can't understand why and I don't know how to debug the issue.
这是我的项目(一个简单的32位控制台应用程序)的主要功能,它读取一个库,将其分配到内存并手动加载它:
Here is the main function of my project (a simple 32bit console application) which reads a library, allocates it into memory and loads it manually:
public static UInt32 Load(String libraryName)
{
if (libraries.ContainsKey(libraryName))
return libraries[libraryName];
String libraryPath = Path.Combine(Environment.SystemDirectory, libraryName);
Byte[] libraryBytes = File.ReadAllBytes(libraryPath);
fixed (Byte* libraryPointer = libraryBytes)
{
HeaderDOS* headerDOS = (HeaderDOS*)libraryPointer;
if ((UInt16)((headerDOS->Magic << 8) | (headerDOS->Magic >> 8)) != IMAGE_DOS_SIGNATURE)
return 0;
HeadersNT* headerNT = (HeadersNT*)(libraryPointer + headerDOS->LFANEW);
UInt32 addressLibrary = VirtualAlloc(headerNT->OptionalHeader.ImageBase, headerNT->OptionalHeader.SizeOfImage, AllocationType.RESERVE, MemoryProtection.READWRITE);
if (addressLibrary == 0)
addressLibrary = VirtualAlloc(0, headerNT->OptionalHeader.SizeOfImage, AllocationType.RESERVE, MemoryProtection.READWRITE);
if (addressLibrary == 0)
return 0;
Library* library = (Library*)Marshal.AllocHGlobal(sizeof(Library));
library->Address = (Byte*)addressLibrary;
library->ModulesCount = 0;
library->Modules = null;
library->Initialized = false;
VirtualAlloc(addressLibrary, headerNT->OptionalHeader.SizeOfImage, AllocationType.COMMIT, MemoryProtection.READWRITE);
UInt32 addressHeaders = VirtualAlloc(addressLibrary, headerNT->OptionalHeader.SizeOfHeaders, AllocationType.COMMIT, MemoryProtection.READWRITE);
MemoryCopy((Byte*)headerDOS, (Byte*)addressHeaders, (headerDOS->LFANEW + headerNT->OptionalHeader.SizeOfHeaders));
library->Headers = (HeadersNT*)((Byte*)addressHeaders + headerDOS->LFANEW);
library->Headers->OptionalHeader.ImageBase = addressLibrary;
CopySections(library, headerNT, libraryPointer);
UInt32 locationDelta = addressLibrary - headerNT->OptionalHeader.ImageBase;
if (locationDelta != 0)
PerformBaseRelocation(library, locationDelta);
UInt32 libraryHandle = (UInt32)library;
if (!BuildImportTable(library))
{
Free(libraryName);
return 0;
}
FinalizeSections(library);
if (library->Headers->OptionalHeader.AddressOfEntryPoint == 0)
{
Free(libraryName);
return 0;
}
UInt32 libraryEntryPoint = addressLibrary + library->Headers->OptionalHeader.AddressOfEntryPoint;
if (libraryEntryPoint == 0)
{
Free(libraryName);
return 0;
}
LibraryMain main = (LibraryMain)Marshal.GetDelegateForFunctionPointer(new IntPtr(libraryEntryPoint), typeof(LibraryMain));
UInt32 result = main(addressLibrary, DLL_PROCESS_ATTACH, 0);
if (result == 0)
{
Free(libraryName);
return 0;
}
library->Initialized = true;
libraries[libraryName] = libraryHandle;
return libraryHandle;
}
}
这里是一个如何使用它的例子:
And here is an example on how to use it:
private const Byte VK_Z_BREAK = 0x5A;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void KeyboardEventDelegate(Byte key, Byte scan, KeyboardFlags flags, Int32 extra);
[Flags]
private enum KeyboardFlags : uint
{
EXTENDEDKEY = 0x0001,
KEYUP = 0x0002,
}
public static void Main()
{
UInt32 libraryHandle = LibraryLoader.Load("User32.dll");
if (libraryHandle != 0)
{
UInt32 functionHandle = LibraryLoader.GetFunctionAddress("User32.dll", "keybd_event");
if (functionHandle != 0)
{
KeyboardEventDelegate s_KeyboardEvent = (KeyboardEventDelegate)Marshal.GetDelegateForFunctionPointer(new IntPtr(functionHandle), typeof(KeyboardEventDelegate));
while (true)
{
s_KeyboardEvent(VK_Z_BREAK, VK_Z_SCANCODE, 0, 0);
s_KeyboardEvent(VK_Z_BREAK, VK_Z_SCANCODE, KeyboardFlags.KEYUP, 0);
}
}
}
Console.ReadLine();
}
如果您想快速尝试,可以从。
If you want to give it a fast try, you can download the project from this link.
经过几次尝试,在调用DllMain之后使用Marshal.GetLastWin32Error(),我发现正在生成一个错误代码14,对应于ERROR_OUTOFMEMORY。如果我继续DllMain调用失败后,我得到一个库函数的地址,尝试使用一个委托调用它产生一个PInvokeStackImbalance异常。有什么线索呢? ^ _ ^
After a few tries, using Marshal.GetLastWin32Error() just after calling DllMain, I discovered that an error code 14 is being produced, which corresponds to ERROR_OUTOFMEMORY. If I go on after the DllMain call failure, and I get the address of a library function, trying to call it using a delegate produces a PInvokeStackImbalance exception. Any clues about this? ^_^
推荐答案
这段代码只是Windows加载器加载DLL的一阶近似值。它只能用于最简单的DLL,从C到C#代码的翻译也很可能会导致麻烦,如堆栈不平衡问题,你正在处理。我看到的主要问题:
This code is only a first-order approximation of what the Windows loader does to load a DLL. It can only work for the simplest of DLLs, the translation from C to C# code is also highly likely to cause trouble like the stack imbalance problem you are dealing with. The chief problems I see:
-
它没有做任何事情来确保DLL还没有加载。当您尝试加载kernel32.dll和user32.dll时,这很有保证是一个麻烦的来源,这些DLL在托管代码开始执行之前已经加载。他们不会再重新加载了。
It doesn't do anything to ensure that the DLL wasn't already loaded before. This is pretty much guaranteed to be a source of trouble when you try to load kernel32.dll and user32.dll, those DLLs are already loaded before managed code starts executing. They will not take kindly to getting loaded again.
它不会做任何明显的事情来确保依赖的DLL被加载,并且他们的DllMain() entrypoints以正确的顺序调用并严格序列化。
It doesn't do anything obvious to ensure that dependent DLLs get loaded as well and that their DllMain() entrypoints are called in the correct order and strictly serialized.
它没有做任何事情来正确处理托管代码加载程序存根,MSCoree.dll,其中使您无法正确加载包含混合模式代码的任何DLL。
It doesn't do anything to properly deal with the managed code loader stub, MSCoree.dll, which makes it very unlikely you can ever properly load any DLLs that contain mixed mode code.
它不会做任何事情来确保Windows加载程序知道这些模块使得很可能对DLL的任何后续请求都将失败。这种失败是不可忽视的。
It doesn't do anything to ensure that the Windows loader is aware of these modules, which makes it very likely that any subsequent request for the DLL will fail. Such failure is quite undiagnosable.
正确解决这些问题的可能性相当低。
The likelihood that you'll be able to address these problems correctly is quite low.
这篇关于从存储器加载库/模块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!