diff --git a/MemoryModule.Tests/Given_MemoryModulePP.cs b/MemoryModule.Tests/Given_MemoryModulePP.cs new file mode 100644 index 0000000..4004bd6 --- /dev/null +++ b/MemoryModule.Tests/Given_MemoryModulePP.cs @@ -0,0 +1,125 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace MemoryModule.Tests +{ + [TestClass] + public class Given_MemoryModulePP + { + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + 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 + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + delegate bool LdrUnloadDllMemoryDelegate([In] IntPtr BaseAddress); + + const uint LOAD_FLAGS_PASS_IMAGE_CHECK = 0x40000000; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + delegate int GetSecretDelegate(); + + private readonly LdrLoadDllMemoryExWDelegate LdrLoadDllMemoryExW; + private readonly LdrUnloadDllMemoryDelegate LdrUnloadDllMemory; + private readonly bool __skip = false; + private readonly NativeAssembly __mmppAsm; + private readonly IntPtr __secretHandle; + + public Given_MemoryModulePP() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + var mmppDll = Helper.GetDllName("MemoryModulePP"); + using var mmppDllStream = Assembly.GetExecutingAssembly() + .GetManifestResourceStream($"{Helper.ModuleName}.{mmppDll}"); + __mmppAsm = NativeAssembly.Load(mmppDllStream, mmppDll); + + LdrLoadDllMemoryExW = + __mmppAsm.GetDelegate("LdrLoadDllMemoryExW"); + LdrUnloadDllMemory = + __mmppAsm.GetDelegate("LdrUnloadDllMemory"); + + var secretDll = Helper.GetDllName("Secret"); + using var secretDllStream = Assembly.GetExecutingAssembly() + .GetManifestResourceStream($"{Helper.ModuleName}.{secretDll}"); + using var secretDllMs = new MemoryStream(); + secretDllStream.CopyTo(secretDllMs); + var secretDllBytes = secretDllMs.ToArray(); + + LdrLoadDllMemoryExW(out __secretHandle, out _, + LOAD_FLAGS_PASS_IMAGE_CHECK, secretDllBytes, 0, "secret", null); + } + else + { + __skip = true; + } + } + + [TestMethod] + public void When_UsedWithDotNetNativeLibrary() + { + Helper.PrintEnvironmentDetails(); + + if (__skip) + { + return; + } + + NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), + (name, asm, path) => + { + if (name == "secret") + { + return __secretHandle; + } + + return IntPtr.Zero; + }); + + for (int i = 0; i < 10; ++i) + { + [DllImport("secret")] + static extern int GetSecret(); + + int result = GetSecret(); + Assert.AreEqual(result, Math.Min(Math.Max(0, result), 100)); + } + } + + [TestMethod] + public void When_UsedWithGetProcAddress() + { + Helper.PrintEnvironmentDetails(); + + if (__skip) + { + return; + } + + { + [DllImport("kernel32.dll", SetLastError = true)] + static extern IntPtr GetProcAddress( + IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName); + + var secretProc = GetProcAddress(__secretHandle, "GetSecret"); + var GetSecret = + Marshal.GetDelegateForFunctionPointer(secretProc); + + for (int i = 0; i < 10; ++i) + { + int result = GetSecret(); + Assert.AreEqual(result, Math.Min(Math.Max(0, result), 100)); + } + } + } + } +} diff --git a/MemoryModule.Tests/LICENSE_MemoryModulePP b/MemoryModule.Tests/LICENSE_MemoryModulePP new file mode 100644 index 0000000..cd214e9 --- /dev/null +++ b/MemoryModule.Tests/LICENSE_MemoryModulePP @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Boring + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/MemoryModule.Tests/MemoryModule.Tests.csproj b/MemoryModule.Tests/MemoryModule.Tests.csproj index 6f4fcf9..bdcf0fb 100644 --- a/MemoryModule.Tests/MemoryModule.Tests.csproj +++ b/MemoryModule.Tests/MemoryModule.Tests.csproj @@ -6,15 +6,6 @@ false - - - - - - - - - @@ -22,6 +13,8 @@ + + diff --git a/MemoryModule.Tests/MemoryModulePP.dll b/MemoryModule.Tests/MemoryModulePP.dll new file mode 100644 index 0000000..bcba770 Binary files /dev/null and b/MemoryModule.Tests/MemoryModulePP.dll differ diff --git a/MemoryModule.Tests/MemoryModulePP64.dll b/MemoryModule.Tests/MemoryModulePP64.dll new file mode 100644 index 0000000..795332a Binary files /dev/null and b/MemoryModule.Tests/MemoryModulePP64.dll differ