9 oct 2023

Funciones de AMSI


¡Muy buenas a todos! Hace unas cuantas publicaciones, hablamos sobre la introducción a AMSI. Hoy vamos a continuar adentrándonos en el mundo de AMSI. En esta ocasión, vamos a profundizar en las funciones que utiliza intérpretes como PowerShell para comunicarse con las medidas de seguridad gracias a la API AMSI.

Dado que AMSI forma parte de la Windows API (user-mode), está documentada por Microsoft para que los desarrolladores no tengan problema en usarla. Es decir, las librerías de la Windows API tienen documentación acerca de cómo se estructuran sus funciones, que parámetros espera cada función, valores que recibe, etc.

En primer lugar, tenemos la función AmsiInitialize. Esta función se llama antes de que ejecutemos cualquier comando en la PowerShell, es decir, esta función se llama en el momento en el que se ejecuta el intérprete. Por lo tanto, es una función complicada para atacar y hacer un bypass, debido a que no se puede modificar la lógica mediante comandos PowerShell.



La documentación de Microsoft nos dice que esta función recibe dos parámetros:

  • El primer parámetro es el nombre de la aplicación que va a utilizar la DLL, que en nuestro caso va a ser PowerShell.

  • El segundo parámetro es un puntero vacío llamado amsiContext. Este puntero será rellenado por la función AmsiInitialize y será utilizado con el resto de las funciones.

Tanto en esta función como en la mayoría de las funciones de AMSI se devuelve HRESULT, que es un código que representa si la función se ha ejecutado de forma exitosa o no. La lista de códigos podemos verla en la documentación de Microsoft.

La siguiente función es AmsiOpenSession. Cada vez que se ejecuta un comando PowerShell, es necesario crear una sesión con AMSI, por lo que se llamará a esta función.



La documentación de Microsoft nos dice que esta función también recibe dos parámetros:

  • El primer argumento es amsiContext, que equivale al contexto que se ha rellenado en la anterior función.

  • El segundo argumento es un puntero vacío llamado amsiSession. Este puntero será rellenado por la función AmsiOpenSession y será utilizado por otras funciones.

Al igual que AmsiInitialize, devuelve un código que representa si la función se ha ejecutado de forma exitosa o no.

En tercer lugar, tenemos AmsiScanString y AmsiScanBuffer. Ambas funciones tienen el mismo cometido y la forma de utilizarse es bastante similar. Tanto es así, que en realidad AmsiScanString llama por debajo a AmsiScanBuffer. La labor de estas funciones es capturar el contenido de un comando para que posteriormente lo escanee el AV/EDR.


La documentación de Microsoft nos dice que AmsiScanString recibe cinco parámetros:

  • El primer argumento representa contexto que ha rellenado la primera función (AmsiInitialize).

  • El segundo argumento es un string, que representa el contenido del comando.

  • El tercer argumento es una especie de identificador.

  • El cuarto argumento es la sesión, que como hemos visto previamente, se ha rellenado en la función AmsiOpenSession.

  • Por último, con el quinto argumento la función recibe un puntero vacío que va a representar el resultado del escaneo (es decir, si el AV/EDR dice que es malware o no). Los valores pueden ser los siguientes:        


Por otro lado, AmsiScanBuffer recibe, en lugar de un string que va a representar el comando, un buffer. Además, también es necesario indicar la longitud de ese buffer. Los demás argumentos son iguales para ambas funciones.

Por último, tanto para AmsiScanString como para AmsiScanBuffer se devuelve HRESULT, que de la misma forma que en las funciones anteriores, representa si la función se ha ejecutado correctamente. Cabe destacar que no hay que confundir el resultado del escaneo con el resultado de la función, que son dos valores completamente diferentes.

A pesar de eso, son valores que sí que son dependientes entre sí. El contenido de result no puede ser AMSI_RESULT_DETECTED, si HRESULT es E_INVALIDARG, puesto que esto significaría que no se ejecutaría el análisis porque los argumentos son inválidos.

Pero si por un casual, por algún tipo de error, el contenido de result representa que existe malware, pero HRESULT es INVALIDARG, el comando se ejecutará con normalidad, ya que primero se comprobará HRESULT, y si este indica que se ha ejecutado la función con normalidad, comprobará el contenido de result.

Por último, tenemos la función AmsiCloseSession, que tiene lugar cuando ya se ha realizado el escaneo. Esta función se llama también cuando se ejecuta un comando en PowerShell. La tarea de esta función es cerrar la sesión para que no se pueda volver a utilizar.



La documentación de Microsoft nos dice que recibe dos parámetros:

  • El contexto de la inicialización. 
  • La sesión que se quiere cerrar.

Al ser una función void, no devuelve nada. Es decir, por parte de Microsoft no existe control de errores para saber si la sesión se ha cerrado con éxito, o ha habido algún tipo de fallo.

Por lo tanto, cuando abrimos una PowerShell, el flujo de llamadas a la API sería el siguiente:



Y en el momento que ya está la PowerShell abierta y ejecutamos comandos, el flujo de llamadas a la API sería el siguiente:


Y por hoy,¡ ya está bien!, continuaremos con AMSI en las próximas entregas.

Juan Gabriel RuizSenior Security Analyst at Zerolynx Justo MartínSecurity Analyst at Zerolynx.




No hay comentarios:

Publicar un comentario