[Feature Request] DLL releases?
Currently, I see that MemoryModulePP has no releases.
Also, it is only built as a static library (.lib).
There are use cases where a .dll of MemoryModulePP might be preferred, such as bootstrapping MemoryModulePP from a less powerful .dll loader.
Hi. MemoryModulePP supports building as a DLL. You only need to select the configuration ending with DLL for MemoryModule in the configuration manager. In addition, MemoryModulePP integrates ReflectiveDllLoader, so it can self-load into a fully functional MemoryModulePP without using a third-party memory loader.
In addition, MemoryModulePP integrates ReflectiveDllLoader, so it can self-load into a fully functional MemoryModulePP without using a third-party memory loader.
I need to initialize the loading from managed (.NET) code, where MemoryModulePP itself and other native .DLLs are embedded as a resource, so there must be at least a managed loader to bootstrap everything before MemoryModulePP's features can be used.
Also, the build configurations do not work from the MSBuild command-line:
Method 1 (Build from solution):
D:\CodingProjects\MemoryModulePP>msbuild MemoryModulePP.sln -target:MemoryModule -property:Configuration=ReleaseDll,Platform=x64
MSBuild version 17.8.3+195e7f5a3 for .NET Framework
Build started 31/12/2023 15:26:14.
Project "D:\CodingProjects\MemoryModulePP\MemoryModulePP.sln" on node 1 (MemoryModule target(s)).
D:\CodingProjects\MemoryModulePP\MemoryModulePP.sln.metaproj : error MSB4126: The specified solution configuration "Rel
easeDll|x64" is invalid. Please specify a valid solution configuration using the Configuration and Platform properties
(e.g. MSBuild.exe Solution.sln /p:Configuration=Debug /p:Platform="Any CPU") or leave those properties blank to use the
default solution configuration. [D:\CodingProjects\MemoryModulePP\MemoryModulePP.sln]
Done Building Project "D:\CodingProjects\MemoryModulePP\MemoryModulePP.sln" (MemoryModule target(s)) -- FAILED.
Build FAILED.
"D:\CodingProjects\MemoryModulePP\MemoryModulePP.sln" (MemoryModule target) (1) ->
(ValidateSolutionConfiguration target) ->
D:\CodingProjects\MemoryModulePP\MemoryModulePP.sln.metaproj : error MSB4126: The specified solution configuration "R
eleaseDll|x64" is invalid. Please specify a valid solution configuration using the Configuration and Platform propertie
s (e.g. MSBuild.exe Solution.sln /p:Configuration=Debug /p:Platform="Any CPU") or leave those properties blank to use t
he default solution configuration. [D:\CodingProjects\MemoryModulePP\MemoryModulePP.sln]
0 Warning(s)
1 Error(s)
Time Elapsed 00:00:00.04
Method 2 (Build from project):
D:\CodingProjects\MemoryModulePP>msbuild MemoryModule\MemoryModule.vcxproj -property:Configuration=ReleaseDll,Platform=x64
MSBuild version 17.8.3+195e7f5a3 for .NET Framework
Build started 31/12/2023 15:28:50.
Project "D:\CodingProjects\MemoryModulePP\MemoryModule\MemoryModule.vcxproj" on node 1 (default targets).
PrepareForBuild:
Structured output is enabled. The formatting of compiler diagnostics will reflect the error hierarchy. See https://ak
a.ms/cpp/structured-output for more details.
InitializeBuildStatus:
Touching "x64\ReleaseDll\MemoryModule.tlog\unsuccessfulbuild".
ClCompile:
C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.38.33130\bin\HostX64\x64\CL.exe /c /Zi /nol
ogo /W3 /WX- /diagnostics:column /sdl /O2 /Oi /GL /D _MEMORY_MODULE /D NDEBUG /D _USRDLL /D _WINDLL /D _UNICODE /D UN
ICODE /Gm- /EHsc /MT /GS /Gy /fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline /permissive- /Fo"x64\ReleaseDll\\" /Fd"x
64\ReleaseDll\vc143.pdb" /external:W3 /Gd /TP /FC /errorReport:queue MmpDotNet.cpp MmpTls.cpp
MmpDotNet.cpp
MmpTls.cpp
D:\CodingProjects\MemoryModulePP\MemoryModule\MmpDotNet.cpp(2,10): error C1083: Cannot open include file: '3rdparty/Det
ours/detours.h': No such file or directory [D:\CodingProjects\MemoryModulePP\MemoryModule\MemoryModule.vcxproj]
D:\CodingProjects\MemoryModulePP\MemoryModule\MmpTls.cpp(9,10): error C1083: Cannot open include file: '3rdparty/Detour
s/detours.h': No such file or directory [D:\CodingProjects\MemoryModulePP\MemoryModule\MemoryModule.vcxproj]
Done Building Project "D:\CodingProjects\MemoryModulePP\MemoryModule\MemoryModule.vcxproj" (default targets) -- FAILED.
Build FAILED.
"D:\CodingProjects\MemoryModulePP\MemoryModule\MemoryModule.vcxproj" (default target) (1) ->
(ClCompile target) ->
D:\CodingProjects\MemoryModulePP\MemoryModule\MmpDotNet.cpp(2,10): error C1083: Cannot open include file: '3rdparty/D
etours/detours.h': No such file or directory [D:\CodingProjects\MemoryModulePP\MemoryModule\MemoryModule.vcxproj]
D:\CodingProjects\MemoryModulePP\MemoryModule\MmpTls.cpp(9,10): error C1083: Cannot open include file: '3rdparty/Deto
urs/detours.h': No such file or directory [D:\CodingProjects\MemoryModulePP\MemoryModule\MemoryModule.vcxproj]
0 Warning(s)
2 Error(s)
Time Elapsed 00:00:00.84
Since the DebugDll and ReleaseDll configurations are only the project configuration of MemoryModule and not the solution's configuration, you cannot build through the solution. Building directly from the project will cause compilation errors because the solution directory environment variable $(SolutionDir) is referenced in the project. The value of this variable needs to be specified explicitly. Example of successful compilation from the command line:
msbuild MemoryModule.vcxproj /p:SolutionDir="MemoryModulePP solution root directory",Configuration=ReleaseDll,Platform=x64
You can use this class to call MemoryModulePP from the .NET platform:
public static class MemoryModulePP
{
[DllImport("kernel32.dll")]
private static extern IntPtr VirtualAlloc(
IntPtr lpAddress,
UIntPtr dwSize,
int flAllocationType,
int flProtect
);
[DllImport("kernel32.dll")]
private static extern int VirtualFree(
IntPtr lpAddress,
UIntPtr dwSize,
int dwFreeType
);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(
IntPtr hModule,
[MarshalAs(UnmanagedType.LPStr)]string lpProcName
);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate IntPtr ReflectiveLoader(IntPtr buffer);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate int LdrLoadDllMemoryExWDelegate(
[Out] out IntPtr BaseAddress,
[Out] out IntPtr LdrEntry,
[In] uint dwFlags,
[In][MarshalAs(UnmanagedType.LPArray)] byte[] BufferAddress,
[In] UIntPtr BufferSize,
[In][MarshalAs(UnmanagedType.LPWStr)] string DllName,
[In][MarshalAs(UnmanagedType.LPWStr)] string DllFullName
);
public static LdrLoadDllMemoryExWDelegate LdrLoadDllMemoryExW;
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate bool LdrUnloadDllMemoryDelegate([In] IntPtr BaseAddress);
public static LdrUnloadDllMemoryDelegate LdrUnloadDllMemory;
public static IntPtr Initialize(byte[] buffer, int offsetOfReflectiveLoader)
{
System.Diagnostics.Debug.Assert(buffer != null && buffer.Length > 0 && offsetOfReflectiveLoader > 0);
IntPtr result = IntPtr.Zero;
var raw = VirtualAlloc(IntPtr.Zero, (UIntPtr)buffer.Length, 0x00001000, 0x40);
if (raw != IntPtr.Zero)
{
Marshal.Copy(buffer, 0, raw, buffer.Length);
var loader = Marshal.GetDelegateForFunctionPointer<ReflectiveLoader>(raw + offsetOfReflectiveLoader);
result = loader(raw);
if (result != IntPtr.Zero)
{
var ptr = GetProcAddress(result, "LdrLoadDllMemoryExW");
if (ptr != IntPtr.Zero)
{
LdrLoadDllMemoryExW = Marshal.GetDelegateForFunctionPointer<LdrLoadDllMemoryExWDelegate>(ptr);
}
ptr = GetProcAddress(result, "LdrUnloadDllMemory");
if (ptr != IntPtr.Zero)
{
LdrUnloadDllMemory = Marshal.GetDelegateForFunctionPointer<LdrUnloadDllMemoryDelegate>(ptr);
}
}
VirtualFree(raw, UIntPtr.Zero, 0x00008000);
}
return result;
}
}
Here is an example of calling the MemoryModulePP class, 0x1221 is the offset of MemoryModule.dll!ReflectiveLoader in the DLL file, which is constant for each MemoryModule.dll built. You can use GetReflectiveLoaderOffset to get this offset.
class Program
{
static T GetProcAddress<T>(IntPtr hModule, string lpName) where T : class
{
var ptr = MemoryModulePP.GetProcAddress(hModule, lpName);
if (ptr != IntPtr.Zero)
{
return Marshal.GetDelegateForFunctionPointer<T>(ptr);
}
return null;
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int _exception(int code);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate int FARPROC();
static void test()
{
byte[] buffer = null;
using (var fs = File.OpenRead("a.dll"))
{
using (var sr = new BinaryReader(fs))
{
buffer = sr.ReadBytes((int)fs.Length);
}
}
var status = MemoryModulePP.LdrLoadDllMemoryExW(out var baseAddress, out _, 0, buffer, UIntPtr.Zero, "kernel64", null);
if (status >= 0)
{
var exception = GetProcAddress<_exception>(baseAddress, "exception");
if (exception != null)
{
for (int i = 0; i < 5; ++i) exception(i);
}
var pfn = GetProcAddress<FARPROC>(baseAddress, "thread");
if (pfn != null && pfn() != 0)
{
Console.WriteLine("thread test failed.\n");
}
MemoryModulePP.LdrUnloadDllMemory(baseAddress);
}
}
static void Main(string[] args)
{
byte[] buffer = null;
using (var fs = File.OpenRead("MemoryModule.dll"))
{
using (var sr = new BinaryReader(fs))
{
buffer = sr.ReadBytes((int)fs.Length);
}
}
var hModule = MemoryModulePP.Initialize(buffer, 0x1221); //
if (hModule != IntPtr.Zero)
{
test();
}
}
}
Since the DebugDll and ReleaseDll configurations are only the project configuration of MemoryModule and not the solution's configuration, you cannot build through the solution. Building directly from the project will cause compilation errors because the solution directory environment variable $(SolutionDir) is referenced in the project. The value of this variable needs to be specified explicitly.
Ahh thanks. This works on the command line:
msbuild MemoryModule\MemoryModule.vcxproj /p:SolutionDir="%cd%",Configuration=ReleaseDll,Platform=Win32
Any plans to support ARM and ARM64 though?
Here is an example of calling the MemoryModulePP class, 0x1221 is the offset of MemoryModule.dll!ReflectiveLoader in the DLL file, which is constant for each MemoryModule.dll built. You can use GetReflectiveLoaderOffset to get this offset.
Looks cool if you want some compact code but...
Allocating virtual memory, copying some bytes, and then parsing the PE to get an exported function's address, like what GetReflectiveLoaderOffset does...
Isn't that what a PE loader like any library of the MemoryModule family does?
Yes, it does the same job as most loaders, just to do it without an external loader. If you don't want to use self-loading, you can use a third-party loader to load it, and then call the ReflectiveMapDll function to initialize the environment necessary for MemoryModulePP.
Hello, I added the configuration for the ARM platform to the solution and successfully ran the tests in a Windows 10 ARM64 simulation environment. I'm not very familiar with the ARM platform, but thanks to the strong compatibility of Windows, I don't need to modify it too much. Just check out the arm branch.