Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request] DLL releases? #39

Open
trungnt2910 opened this issue Dec 30, 2023 · 9 comments
Open

[Feature Request] DLL releases? #39

trungnt2910 opened this issue Dec 30, 2023 · 9 comments

Comments

@trungnt2910
Copy link

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.

@bb107
Copy link
Owner

bb107 commented Dec 31, 2023

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.
cm

@trungnt2910
Copy link
Author

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.

@trungnt2910
Copy link
Author

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

@bb107
Copy link
Owner

bb107 commented Dec 31, 2023

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

@bb107
Copy link
Owner

bb107 commented Dec 31, 2023

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;
        }
    }

@bb107
Copy link
Owner

bb107 commented Dec 31, 2023

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();
            }
        }
    }

@trungnt2910
Copy link
Author

trungnt2910 commented Dec 31, 2023

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?

@bb107
Copy link
Owner

bb107 commented Dec 31, 2023

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.

bb107 added a commit that referenced this issue Jan 4, 2024
@bb107
Copy link
Owner

bb107 commented Jan 4, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants